文章大致内容介绍

本文主要讲述播放音乐流程,AudioResampler是如何生效的

  • 播放流程
  • AudioMixer分析
  • 从MixerThread分析

播放流程

在Android中间,如果使用硬解码OffloadThread是不会出现混频和重采样的,但是播放需要软解的音频,则会按需要重采样。
为了方便研究,这里修改软件配置文件,让primary compress_offload仅仅支持8000的采样率播放,音乐播放会去跑MixerThread线程,这个线程上面才会出现重采样。
configs/msm8909/audio_policy_configuration.xml修改如下:

@@ -69,7 +69,7 @@<mixPort name="compressed_offload" role="source"flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING"><profile name="" format="AUDIO_FORMAT_MP3"
-                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                             samplingRates="8000"channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_MONO"/>

使用播放流程如下:

AudioMixer分析:

我们从log出发,查看代码调用流程

AudioMixer: add track (0)
AudioMixer::prepareForDownmix(0xb4f7d060) with mask 0x3
AudioMixer::prepareForReformat(0xb4f7d060) with format 0x1
AudioMixer: enable(0) AudioMixer: setParameter(VOLUME, VOLUME0: 0000)
AudioMixer: setParameter(VOLUME, VOLUME1: 0000)
AudioMixer: Creating resampler from track 44100 Hz to device 48000 Hz
AudioResampler: resampler load 0 -> 6 MHz due to delta +6 MHz from quality 6
AudioResampler: Create dynamic Resampler = 6
AudioMixer: setParameter(RESAMPLE, SAMPLE_RATE, 44100) AudioMixer: setParameter(TRACK, MIXER_FORMAT, 0x5) AudioMixer: setParameter(TRACK, MAIN_BUFFER, 0xb4f7a000) AudioMixer: mixer configuration change: 1
AudioMixer:activeTracks (00000001) all16BitsStereoNoResample=0, resampling=1, volumeRamp=0
AudioMixer: track__Resample

这段log的调用流程

1,AudioFlinger::MixerThread::checkForNewParameter_l,
2,AudioFlinger::MixerThread::getTrackName_l
3, AudioMixer::getTrackName(audio_channel_mask_t
channelMask,audio_format_t format, int sessionId)
4,调用prepare处理

prepareForDownmix这个函数,是用来处理多通道,将多通道转化,按照条件选择DownMixer或者Remixer,函数如下:

status_t AudioMixer::track_t::prepareForDownmix()
{ALOGV("AudioMixer::prepareForDownmix(%p) with mask 0x%x",this, channelMask);// discard the previous downmixer if there was oneunprepareForDownmix();// MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks// are not the same and not handled internally, as mono -> stereo currently is.if (channelMask == mMixerChannelMask|| (channelMask == AUDIO_CHANNEL_OUT_MONO&& mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) {//mMixerChannelMask 在getTrackName中间被赋值为双声道,判断是否需要downMixerreturn NO_ERROR;}// DownmixerBufferProvider is only used for position masks.if (audio_channel_mask_get_representation(channelMask)== AUDIO_CHANNEL_REPRESENTATION_POSITION&& DownmixerBufferProvider::isMultichannelCapable()) {//条件满足channelMask&0x3为0,即声道数大于2,并且系统支持兼容多声道,则初始化相应的bufferprovider用来改变通道数DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(channelMask,mMixerChannelMask,AUDIO_FORMAT_PCM_16_BIT /* TODO: use mMixerInFormat, now only PCM 16 */,sampleRate, sessionId, kCopyBufferFrameCount);if (pDbp->isValid()) { // if constructor completed properlymDownmixRequiresFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmixdownmixerBufferProvider = pDbp;reconfigureBufferProviders();return NO_ERROR;}delete pDbp;}//使用remixer的情况// Effect downmixer does not accept the channel conversion.  Let's use our remixer.RemixBufferProvider* pRbp = new RemixBufferProvider(channelMask,mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount);// Remix always finds a conversion whereas Downmixer effect above may fail.downmixerBufferProvider = pRbp;reconfigureBufferProviders();return NO_ERROR;
}

prepareForReformat()判断是否需要格式转换:


status_t AudioMixer::track_t::prepareForReformat()
{ALOGV("AudioMixer::prepareForReformat(%p) with format %#x", this, mFormat);// discard previous reformattersunprepareForReformat();// only configure reformatters as neededconst audio_format_t targetFormat = mDownmixRequiresFormat != AUDIO_FORMAT_INVALID? mDownmixRequiresFormat : mMixerInFormat;bool requiresReconfigure = false;if (mFormat != targetFormat) {mReformatBufferProvider = new ReformatBufferProvider(audio_channel_count_from_out_mask(channelMask),mFormat,targetFormat,kCopyBufferFrameCount);requiresReconfigure = true;}if (targetFormat != mMixerInFormat) {mPostDownmixReformatBufferProvider = new ReformatBufferProvider(audio_channel_count_from_out_mask(mMixerChannelMask),targetFormat,mMixerInFormat,kCopyBufferFrameCount);requiresReconfigure = true;}if (requiresReconfigure) {reconfigureBufferProviders();}return NO_ERROR;
}

从AudioMixer到AudioResampler

调用的时序图如下:

voidAudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
它是把各个track的声音数据相加。所谓声音数据,可以认为是一个个的采样点,Android默认支持的采样精度是16bit的,格式为signedPCM,所以每个采样点用有符号的16位数int16_t表示。如果直接加16bit的数据,肯定会造成16bit的值溢出,Android的做法是强转成int32_t,相加,并把和赋值给了32bit的数。注意,相加前乘上了音量,而表达音量的数据类型也是int32_t。这样,就能保证在这个过程中是不会溢出的。

void ditherAndClamp(int32_t* out, constint32_t *sums, size_t c) 将32bit的输出转成16bit,再把右声道放高16bit,左声道放低16bit输出

AudioMixer->AudioResampler传递设置参数处理主要的函数:prepareTracks_l()


这个函数中设置重采样参数等操作
// prepareTracks_l() must be called with ThreadBase::mLock held
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(Vector< sp<Track> > *tracksToRemove)
{mixer_state mixerStatus = MIXER_IDLE;// find out which tracks need to be processedsize_t count = mActiveTracks.size();size_t mixedTracks = 0;size_t tracksWithEffect = 0;// counts only _active_ fast trackssize_t fastTracks = 0;uint32_t resetMask = 0; // bit mask of fast tracks that need to be resetfloat masterVolume = mMasterVolume;bool masterMute = mMasterMute;
#ifdef DOLBY_ENABLE // DOLBY_DAP_PREGAIN// The maximum volume of left channel and right channel for pregain calculation.uint32_t max_vol = 0;float dvlf = 0.0f;float dvrf = 0.0f;
#endif // DOLBY_ENDif (masterMute) {masterVolume = 0;}// Delegate master volume control to effect in output mix effect chain if neededsp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);if (chain != 0) {uint32_t v = (uint32_t)(masterVolume * (1 << 24));chain->setVolume_l(&v, &v);masterVolume = (float)((v + (1 << 23)) >> 24);chain.clear();}// prepare a new state to pushFastMixerStateQueue *sq = NULL;FastMixerState *state = NULL;bool didModify = false;FastMixerStateQueue::block_t block = FastMixerStateQueue::BLOCK_UNTIL_PUSHED;if (mFastMixer != 0) {sq = mFastMixer->sq();state = sq->begin();}mMixerBufferValid = false;  // mMixerBuffer has no valid data until appropriate tracks found.mEffectBufferValid = false; // mEffectBuffer has no valid data until tracks found.for (size_t i=0 ; i<count ; i++) {const sp<Track> t = mActiveTracks[i].promote();if (t == 0) {continue;}// this const just means the local variable doesn't changeTrack* const track = t.get();// process fast tracksif (track->isFastTrack()) {// It's theoretically possible (though unlikely) for a fast track to be created// and then removed within the same normal mix cycle.  This is not a problem, as// the track never becomes active so it's fast mixer slot is never touched.// The converse, of removing an (active) track and then creating a new track// at the identical fast mixer slot within the same normal mix cycle,// is impossible because the slot isn't marked available until the end of each cycle.int j = track->mFastIndex;ALOG_ASSERT(0 < j && j < (int)FastMixerState::sMaxFastTracks);ALOG_ASSERT(!(mFastTrackAvailMask & (1 << j)));FastTrack *fastTrack = &state->mFastTracks[j];// Determine whether the track is currently in underrun condition,// and whether it had a recent underrun.FastTrackDump *ftDump = &mFastMixerDumpState.mTracks[j];FastTrackUnderruns underruns = ftDump->mUnderruns;uint32_t recentFull = (underruns.mBitFields.mFull -track->mObservedUnderruns.mBitFields.mFull) & UNDERRUN_MASK;uint32_t recentPartial = (underruns.mBitFields.mPartial -track->mObservedUnderruns.mBitFields.mPartial) & UNDERRUN_MASK;uint32_t recentEmpty = (underruns.mBitFields.mEmpty -track->mObservedUnderruns.mBitFields.mEmpty) & UNDERRUN_MASK;uint32_t recentUnderruns = recentPartial + recentEmpty;track->mObservedUnderruns = underruns;// don't count underruns that occur while stopping or pausing// or stopped which can occur when flush() is called while activeif (!(track->isStopping() || track->isPausing() || track->isStopped()) &&recentUnderruns > 0) {// FIXME fast mixer will pull & mix partial buffers, but we count as a full underruntrack->mAudioTrackServerProxy->tallyUnderrunFrames(recentUnderruns * mFrameCount);} else {track->mAudioTrackServerProxy->tallyUnderrunFrames(0);}// This is similar to the state machine for normal tracks,// with a few modifications for fast tracks.bool isActive = true;switch (track->mState) {case TrackBase::STOPPING_1:// track stays active in STOPPING_1 state until first underrunif (recentUnderruns > 0 || track->isTerminated()) {track->mState = TrackBase::STOPPING_2;}break;case TrackBase::PAUSING:// ramp down is not yet implementedtrack->setPaused();break;case TrackBase::RESUMING:// ramp up is not yet implementedtrack->mState = TrackBase::ACTIVE;break;case TrackBase::ACTIVE:if (recentFull > 0 || recentPartial > 0) {// track has provided at least some frames recently: reset retry counttrack->mRetryCount = kMaxTrackRetries;}if (recentUnderruns == 0) {// no recent underruns: stay activebreak;}// there has recently been an underrun of some kindif (track->sharedBuffer() == 0) {// were any of the recent underruns "empty" (no frames available)?if (recentEmpty == 0) {// no, then ignore the partial underruns as they are allowed indefinitelybreak;}// there has recently been an "empty" underrun: decrement the retry counterif (--(track->mRetryCount) > 0) {break;}// indicate to client process that the track was disabled because of underrun;// it will then automatically call start() when data is availabletrack->disable();// remove from active list, but state remains ACTIVE [confusing but true]isActive = false;break;}// fall throughcase TrackBase::STOPPING_2:case TrackBase::PAUSED:case TrackBase::STOPPED:case TrackBase::FLUSHED:   // flush() while active// Check for presentation complete if track is inactive// We have consumed all the buffers of this track.// This would be incomplete if we auto-paused on underrun{size_t audioHALFrames =(mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;int64_t framesWritten = mBytesWritten / mFrameSize;if (!(mStandby || track->presentationComplete(framesWritten, audioHALFrames))) {// track stays in active list until presentation is completebreak;}}if (track->isStopping_2()) {track->mState = TrackBase::STOPPED;}if (track->isStopped()) {// Can't reset directly, as fast mixer is still polling this track//   track->reset();// So instead mark this track as needing to be reset after push with ackresetMask |= 1 << i;}isActive = false;break;case TrackBase::IDLE:default:LOG_ALWAYS_FATAL("unexpected track state %d", track->mState);}if (isActive) {// was it previously inactive?if (!(state->mTrackMask & (1 << j))) {ExtendedAudioBufferProvider *eabp = track;VolumeProvider *vp = track;fastTrack->mBufferProvider = eabp;fastTrack->mVolumeProvider = vp;fastTrack->mChannelMask = track->mChannelMask;fastTrack->mFormat = track->mFormat;fastTrack->mGeneration++;state->mTrackMask |= 1 << j;didModify = true;// no acknowledgement required for newly active tracks}// cache the combined master volume and stream type volume for fast mixer; this// lacks any synchronization or barrier so VolumeProvider may read a stale valuetrack->mCachedVolume = masterVolume * mStreamTypes[track->streamType()].volume;++fastTracks;} else {// was it previously active?if (state->mTrackMask & (1 << j)) {fastTrack->mBufferProvider = NULL;fastTrack->mGeneration++;state->mTrackMask &= ~(1 << j);didModify = true;// If any fast tracks were removed, we must wait for acknowledgement// because we're about to decrement the last sp<> on those tracks./* MODIFIED-BEGIN by baowen.zhang, 2016-12-22,BUG-3773166*/// If the fast track was added when output was already suspended// and removed before the output resumed, which means fastmixer never// runs, so no need to wait for acknowledgement for this case.if (state->mCommand & FastMixerState::MIX_WRITE)block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;/* MODIFIED-END by baowen.zhang,BUG-3773166*/} else {LOG_ALWAYS_FATAL("fast track %d should have been active; ""mState=%d, mTrackMask=%#x, recentUnderruns=%u, isShared=%d",j, track->mState, state->mTrackMask, recentUnderruns,track->sharedBuffer() != 0);}tracksToRemove->add(track);// Avoids a misleading display in dumpsystrack->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL;}continue;}{   // local variable scope to avoid goto warningaudio_track_cblk_t* cblk = track->cblk();// The first time a track is added we wait// for all its buffers to be filled before processing itint name = track->name();// make sure that we have enough frames to mix one full buffer.// enforce this condition only once to enable draining the buffer in case the client// app does not call stop() and relies on underrun to stop:// hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed// during last roundsize_t desiredFrames;const uint32_t sampleRate = track->mAudioTrackServerProxy->getSampleRate();AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();desiredFrames = sourceFramesNeededWithTimestretch(sampleRate, mNormalFrameCount, mSampleRate, playbackRate.mSpeed);// TODO: ONLY USED FOR LEGACY RESAMPLERS, remove when they are removed.// add frames already consumed but not yet released by the resampler// because mAudioTrackServerProxy->framesReady() will include these framesdesiredFrames += mAudioMixer->getUnreleasedFrames(track->name());uint32_t minFrames = 1;if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {minFrames = desiredFrames;}size_t framesReady = track->framesReady();if (ATRACE_ENABLED()) {// I wish we had formatted trace nameschar traceName[16];strcpy(traceName, "nRdy");int name = track->name();if (AudioMixer::TRACK0 <= name &&name < (int) (AudioMixer::TRACK0 + AudioMixer::MAX_NUM_TRACKS)) {name -= AudioMixer::TRACK0;traceName[4] = (name / 10) + '0';traceName[5] = (name % 10) + '0';} else {traceName[4] = '?';traceName[5] = '?';}traceName[6] = '\0';ATRACE_INT(traceName, framesReady);}if ((framesReady >= minFrames) && track->isReady() &&!track->isPaused() && !track->isTerminated()){ALOGVV("track %d s=%08x [OK] on thread %p", name, cblk->mServer, this);mixedTracks++;// track->mainBuffer() != mSinkBuffer or mMixerBuffer means// there is an effect chain connected to the trackchain.clear();if (track->mainBuffer() != mSinkBuffer &&track->mainBuffer() != mMixerBuffer) {if (mEffectBufferEnabled) {mEffectBufferValid = true; // Later can set directly.}chain = getEffectChain_l(track->sessionId());// Delegate volume control to effect in track effect chain if neededif (chain != 0) {tracksWithEffect++;} else {ALOGW("prepareTracks_l(): track %d attached to effect but no chain found on ""session %d",name, track->sessionId());}}int param = AudioMixer::VOLUME;if (track->mFillingUpStatus == Track::FS_FILLED) {// no ramp for the first volume settingtrack->mFillingUpStatus = Track::FS_ACTIVE;if (track->mState == TrackBase::RESUMING) {track->mState = TrackBase::ACTIVE;param = AudioMixer::RAMP_VOLUME;}mAudioMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);// FIXME should not make a decision based on mServer} else if (cblk->mServer != 0) {// If the track is stopped before the first frame was mixed,// do not apply rampparam = AudioMixer::RAMP_VOLUME;}// compute volume for this trackuint32_t vl, vr;       // in U8.24 integer formatfloat vlf, vrf, vaf;   // in [0.0, 1.0] float formatif (track->isPausing() || mStreamTypes[track->streamType()].mute) {vl = vr = 0;vlf = vrf = vaf = 0.;if (track->isPausing()) {track->setPaused();}} else {// read original volumes with volume controlfloat typeVolume = mStreamTypes[track->streamType()].volume;float v = masterVolume * typeVolume;AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;gain_minifloat_packed_t vlr = proxy->getVolumeLR();vlf = float_from_gain(gain_minifloat_unpack_left(vlr));vrf = float_from_gain(gain_minifloat_unpack_right(vlr));// track volumes come from shared memory, so can't be trusted and must be clampedif (vlf > GAIN_FLOAT_UNITY) {ALOGV("Track left volume out of range: %.3g", vlf);vlf = GAIN_FLOAT_UNITY;}if (vrf > GAIN_FLOAT_UNITY) {ALOGV("Track right volume out of range: %.3g", vrf);vrf = GAIN_FLOAT_UNITY;}// now apply the master volume and stream type volumevlf *= v;vrf *= v;// assuming master volume and stream type volume each go up to 1.0,// then derive vl and vr as U8.24 versions for the effect chainconst float scaleto8_24 = MAX_GAIN_INT * MAX_GAIN_INT;vl = (uint32_t) (scaleto8_24 * vlf);vr = (uint32_t) (scaleto8_24 * vrf);// vl and vr are now in U8.24 formatuint16_t sendLevel = proxy->getSendLevel_U4_12();// send level comes from shared memory and so may be corruptif (sendLevel > MAX_GAIN_INT) {ALOGV("Track send level out of range: %04X", sendLevel);sendLevel = MAX_GAIN_INT;}// vaf is represented as [0.0, 1.0] float by rescaling sendLevelvaf = v * sendLevel * (1. / MAX_GAIN_INT);}#ifdef DOLBY_ENABLE // DOLBY_DAP_PREGAINdvlf = vlf;dvrf = vrf;
#endif // DOLBY_END// Delegate volume control to effect in track effect chain if neededif (chain != 0 && chain->setVolume_l(&vl, &vr)) {// Do not ramp volume if volume is controlled by effectparam = AudioMixer::VOLUME;// Update remaining floating point volume levelsvlf = (float)vl / (1 << 24);vrf = (float)vr / (1 << 24);track->mHasVolumeController = true;} else {// force no volume ramp when volume controller was just disabled or removed// from effect chain to avoid volume spikeif (track->mHasVolumeController) {param = AudioMixer::VOLUME;}track->mHasVolumeController = false;}
#ifdef DOLBY_ENABLE // DOLBY_DAP_PREGAIN// Select the maximum volume by scanning all the active audio tracks but not the output one.if (!track->isOutputTrack() && !EffectDapController::instance()->bypassTrack(track)) {if (track->mHasVolumeController) {const float scaleto8_24 = MAX_GAIN_INT * MAX_GAIN_INT;max_vol = max(max_vol, max(((uint32_t) (scaleto8_24 * vlf * dvlf)), ((uint32_t) (scaleto8_24 * vrf * dvrf))));} else {max_vol = max(max_vol, max(vl, vr));}}
#endif // DOLBY_END// XXX: these things DON'T need to be done each timemAudioMixer->setBufferProvider(name, track);mAudioMixer->enable(name);mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf);mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::FORMAT, (void *)track->format());mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mChannelMask);// limit track sample rate to 2 x output sample rate, which changes at re-configurationuint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX;uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate();if (reqSampleRate == 0) {reqSampleRate = mSampleRate;} else if (reqSampleRate > maxSampleRate) {reqSampleRate = maxSampleRate;}mAudioMixer->setParameter(name,AudioMixer::RESAMPLE,AudioMixer::SAMPLE_RATE,(void *)(uintptr_t)reqSampleRate);AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();mAudioMixer->setParameter(name,AudioMixer::TIMESTRETCH,AudioMixer::PLAYBACK_RATE,&playbackRate);/** Select the appropriate output buffer for the track.** Tracks with effects go into their own effects chain buffer* and from there into either mEffectBuffer or mSinkBuffer.** Other tracks can use mMixerBuffer for higher precision* channel accumulation.  If this buffer is enabled* (mMixerBufferEnabled true), then selected tracks will accumulate* into it.**/if (mMixerBufferEnabled&& (track->mainBuffer() == mSinkBuffer|| track->mainBuffer() == mMixerBuffer)) {mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer);// TODO: override track->mainBuffer()?mMixerBufferValid = true;} else {mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::MIXER_FORMAT, (void *)AUDIO_FORMAT_PCM_16_BIT);mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());}mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());// reset retry counttrack->mRetryCount = kMaxTrackRetries;// If one track is ready, set the mixer ready if://  - the mixer was not ready during previous round OR//  - no other track is not readyif (mMixerStatusIgnoringFastTracks != MIXER_TRACKS_READY ||mixerStatus != MIXER_TRACKS_ENABLED) {mixerStatus = MIXER_TRACKS_READY;}} else {if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {ALOGV("track(%p) underrun,  framesReady(%zu) < framesDesired(%zd)",track, framesReady, desiredFrames);track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);} else {track->mAudioTrackServerProxy->tallyUnderrunFrames(0);}// clear effect chain input buffer if an active track underruns to avoid sending// previous audio buffer again to effectschain = getEffectChain_l(track->sessionId());if (chain != 0) {chain->clearInputBuffer();}ALOGVV("track %d s=%08x [NOT READY] on thread %p", name, cblk->mServer, this);if ((track->sharedBuffer() != 0) || track->isTerminated() ||track->isStopped() || track->isPaused()) {// We have consumed all the buffers of this track.// Remove it from the list of active tracks.// TODO: use actual buffer filling status instead of latency when available from// audio HALsize_t audioHALFrames = (latency_l() * mSampleRate) / 1000;int64_t framesWritten = mBytesWritten / mFrameSize;if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {if (track->isStopped()) {track->reset();}
#ifdef DOLBY_ENABLE // DOLBY_UDC_VIRTUALIZE_AUDIO// Since this track is being removed from active tracks, check if we// should re-enable content processing in DAP.EffectDapController::instance()->trackStateChanged(track->mId, track->mState);
#endif // DOLBY_ENDtracksToRemove->add(track);}} else {// No buffers for this track. Give it a few chances to// fill a buffer, then remove it from active list.if (--(track->mRetryCount) <= 0) {ALOGI("BUFFER TIMEOUT: remove(%d) from active list on thread %p", name, this);tracksToRemove->add(track);// indicate to client process that the track was disabled because of underrun;// it will then automatically call start() when data is availabletrack->disable();// If one track is not ready, mark the mixer also not ready if://  - the mixer was ready during previous round OR//  - no other track is ready} else if (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY ||mixerStatus != MIXER_TRACKS_READY) {mixerStatus = MIXER_TRACKS_ENABLED;}}mAudioMixer->disable(name);}}   // local variable scope to avoid goto warning}// Push the new FastMixer state if necessarybool pauseAudioWatchdog = false;if (didModify) {state->mFastTracksGen++;// if the fast mixer was active, but now there are no fast tracks, then put it in cold idleif (kUseFastMixer == FastMixer_Dynamic &&/* MODIFIED-BEGIN by baowen.zhang, 2016-12-22,BUG-3773166*///state->mCommand == FastMixerState::MIX_WRITE && state->mTrackMask <= 1) {(state->mCommand & FastMixerState::MIX_WRITE) && state->mTrackMask <= 1) {/* MODIFIED-END by baowen.zhang,BUG-3773166*/state->mCommand = FastMixerState::COLD_IDLE;state->mColdFutexAddr = &mFastMixerFutex;state->mColdGen++;mFastMixerFutex = 0;if (kUseFastMixer == FastMixer_Dynamic) {mNormalSink = mOutputSink;}// If we go into cold idle, need to wait for acknowledgement// so that fast mixer stops doing I/O.block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;pauseAudioWatchdog = true;}}if (sq != NULL) {sq->end(didModify);sq->push(block);}
#ifdef AUDIO_WATCHDOGif (pauseAudioWatchdog && mAudioWatchdog != 0) {mAudioWatchdog->pause();}
#endif// Now perform the deferred reset on fast tracks that have stoppedwhile (resetMask != 0) {size_t i = __builtin_ctz(resetMask);ALOG_ASSERT(i < count);resetMask &= ~(1 << i);sp<Track> t = mActiveTracks[i].promote();if (t == 0) {continue;}Track* track = t.get();ALOG_ASSERT(track->isFastTrack() && track->isStopped());track->reset();}// remove all the tracks that need to be...removeTracks_l(*tracksToRemove);if (getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX) != 0) {mEffectBufferValid = true;}if (mEffectBufferValid) {// as long as there are effects we should clear the effects buffer, to avoid// passing a non-clean buffer to the effect chainmemset(mEffectBuffer, 0, mEffectBufferSize);}// sink or mix buffer must be cleared if all tracks are connected to an// effect chain as in this case the mixer will not write to the sink or mix buffer// and track effects will accumulate into itif ((mBytesRemaining == 0) && ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||(mixedTracks == 0 && fastTracks > 0))) {// FIXME as a performance optimization, should remember previous zero statusif (mMixerBufferValid) {memset(mMixerBuffer, 0, mMixerBufferSize);// TODO: In testing, mSinkBuffer below need not be cleared because// the PlaybackThread::threadLoop() copies mMixerBuffer into mSinkBuffer// after mixing.//// To enforce this guarantee:// ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||// (mixedTracks == 0 && fastTracks > 0))// must imply MIXER_TRACKS_READY.// Later, we may clear buffers regardless, and skip much of this logic.}// FIXME as a performance optimization, should remember previous zero statusmemset(mSinkBuffer, 0, mNormalFrameCount * mFrameSize);}// if any fast tracks, then status is readymMixerStatusIgnoringFastTracks = mixerStatus;if (fastTracks > 0) {mixerStatus = MIXER_TRACKS_READY;}
#ifdef DOLBY_ENABLE// DOLBY_DAP_BYPASS_SOUND_TYPESEffectDapController::instance()->checkForBypass(mActiveTracks, mOutput->flags, mId);// DOLBY_DAP_PREGAIN// Skip the DS pregain setting if there're no active tracks, or all the active tracks are pausing ones,// so that the last pregain will be adopted and zero volume level will not be sent in the 2 cases above.if (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY) {EffectDapController::instance()->updatePregain(mType, mId, mOutput->flags, max_vol);}
#endif // DOLBY_ENDreturn mixerStatus;
}

AudioTrack.cpp
createTrack_l():
// mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFlags
// mFlags is also assigned by createTrack_l(), but not the bit we care about.

set():
initializing a shared buffer AudioTrack via set()

【Android多媒体(重采样与混频)】从playback流程分析AudioResampler,AudioMixer相关推荐

  1. 高通Android智能平台环境搭建_编译流程分析

    高通Android智能平台环境搭建_编译流程分析 高通平台环境搭建,编译,系统引导流程分析 TOC \o \h \z \u 1. 高通平台android开发总结. 7 1.1 搭建高通平台环境开发环境 ...

  2. Android 4.0按键事件以及系统流程分析

    Android 4.0中按键的处理流程 按键在Android系统中,有着不同的代表意义.以前的全键盘的手机代码没有阅读过,所以也不是很了解.本人介绍的是在触摸屏的手机上的按键消息的处理流程. 在现在触 ...

  3. 高通android智能平台环境搭建_编译流程分析,高通平台环境搭建,编译,系统引导流程分析参考...

    高通有两个cpu,他们分别跑不同的系统,应用程序(ap)端是android系统,modem 端是高通自己的系统. 要编译出可供烧写使用的镜像文件需要三部分代码: 1) 获取经过高通打补丁的 andro ...

  4. Android 5.1 长按power键流程分析

    安全模式简述 android平台,在长按power / menu键时会快速进入一个模式选择,部分定制的平台是直接进入安装模式,也可以定制成公司需要的一些特定功能模式,比如报警 ... power 也属 ...

  5. Android系统的心脏-Zygote进程启动流程分析

    简介: Android中,Zygote是整个Android系统的核心进程,是Android系统的心脏.所有的Android应用程序,包括Android框架层所在的进程system_server,都是由 ...

  6. Android 5.1 Phone 挂断电话流程分析

    写在前面的话 本文主要分析Android挂断电话的流程,研究的代码是Android 5.1的,以CDMA为例,GSM同理. 挂断电话主要分两种情况: 本地主动挂断电话 \color{red}{本地主动 ...

  7. Android AOSP 6.0.1 registerReceiver广播注册流程分析

    广播作为 Android 开发的四大组间之一,当我们发送广播以后,发生了什么?广播接收者最终如何收到了广播. 一.复盘广播的使用 在 Android 开发中使用广播分为三个步骤: 1.新建广播接收者 ...

  8. 拨号流程分析(第一篇)

    本文基于Android 8.0 Dialer到Telecom流程 打开手机通话应用的拨号盘 进入拨号界面DialtactsActivity placeCall方法执行 Dialer拨号流程图 备注 打 ...

  9. android车载系统测试,一种车载Android多媒体主机的自动测试方法和系统与流程

    本发明涉及车载多媒体主机的测试方法和装置领域,特别是涉及一种车载Android多媒体主机的自动测试方法和系统. 背景技术: 目前在车载多媒体主机的研发过程中,对车载多媒体主机进行压力测试是不可缺省的一 ...

最新文章

  1. SingleCellExperiment类使用
  2. 图片上传之FileAPI与NodeJs
  3. 算术基本定理证明用计算机,良序原理:算术基本定理的证明
  4. 处理Img标签中src无效时出现的border
  5. 示范NTFS 卷上的硬链接
  6. python从多层循环嵌套中退出_python中退出多层循环的方法
  7. 如何制定好的方案之四:执行力是决定因素
  8. ? php 为啥报错,如何解决js里面的php代码报错问题
  9. std::move()源码分析
  10. SQL Server如何存储特殊字符、上标、下标
  11. matlab 流星雨,dijkstra算法及其matlab实现
  12. 计算机地质制图CAD,CAD地质制图线型
  13. ubuntu安装atat
  14. 搬: 含有对象成员指针的类的对象成员指针初始化问题
  15. 稳压电源: 电路图及类型
  16. 服务器托管的费用介绍
  17. 搜索引擎营销的基本概念和特点是什么?
  18. 使用中文维基百科语料库训练一个word2vec模型
  19. fni matlab,使用Matlab求解Van Der Pol方程的方法研究
  20. linux怎么进入bios界面,联想怎么进入bios界面,详细教您进入联想电脑怎么进入bios界面...

热门文章

  1. pmbok电子版_用技巧方法,让PMBOK知识点烂熟于心
  2. tf.gradients()和grad_ys的作用
  3. crosswalk 初步使用
  4. 迷你四旋翼无人机设计制造(4)——7自由度摇杆遥控器
  5. adobe premiere pro CS6 安装失败解决
  6. 故障管理系统,如何使用二维码报修
  7. 中美线径对照表 AWG和载流说明
  8. SOLIDWORKS Flow Simulation卡门涡街模拟
  9. 计算机考试中英文打字题,五笔及中英文打字试题()含答案.doc
  10. 数据中台到底是解决什么问题的