Android10 音频系统之PlaybackThread

        

        

 App应用播放时,每个APP端都会创建AudioTrack,每个AudioTrack都会通过共享内存与播放线程的Track传递数据,每个应用发送的音频数据格式可能都不相同,而声卡仅支持固定的几种格式,除非发送的格式就是 声卡本身支持的格式,否则这里都需要进行重采样/混音,Playbackthread中使用mAudioMixer的一些操作把硬件不支持的音频格式转化为硬件支持的音频格式,这个过程叫做重采样。

        

xref: /frameworks/av/services/audioflinger/Threads.cpp

bool AudioFlinger::PlaybackThread::threadLoop()
{
    
	.......................................................................................

    // loopCount is used for statistics and diagnostics.
    for (int64_t loopCount = 0; !exitPending(); ++loopCount)
    {
        

        { // scope for mLock

			...............................................................................
			
            // mMixerStatusIgnoringFastTracks is also updated internally
			// 混音前的准备工作
            mMixerStatus = prepareTracks_l(&tracksToRemove);

            mActiveTracks.updatePowerState(this);

            updateMetadata_l();

            // prevent any changes in effect chain list and in each effect chain
            // during mixing and effect process as the audio buffers could be deleted
            // or modified if an effect is created or deleted
            lockEffectChains_l(effectChains);

            // Determine which session to pick up haptic data.
            // This must be done under the same lock as prepareTracks_l().
            // TODO: Write haptic data directly to sink buffer when mixing.
            if (mHapticChannelCount > 0 && effectChains.size() > 0) {
                for (const auto& track : mActiveTracks) {
                    if (track->getHapticPlaybackEnabled()) {
                        activeHapticSessionId = track->sessionId();
                        break;
                    }
                }
            }

            // Acquire a local copy of active tracks with lock (release w/o lock).
            //
            // Control methods on the track acquire the ThreadBase lock (e.g. start()
            // stop(), pause(), etc.), but the threadLoop is entitled to call audio
            // data / buffer methods on tracks from activeTracks without the ThreadBase lock.
            activeTracks.insert(activeTracks.end(), mActiveTracks.begin(), mActiveTracks.end());
        } // mLock scope ends

        if (mBytesRemaining == 0) {
            mCurrentWriteLength = 0;
            if (mMixerStatus == MIXER_TRACKS_READY) {
                // threadLoop_mix() sets mCurrentWriteLength
				// 混音
                threadLoop_mix();
            } else if ((mMixerStatus != MIXER_DRAIN_TRACK)
                        && (mMixerStatus != MIXER_DRAIN_ALL)) {
                // threadLoop_sleepTime sets mSleepTimeUs to 0 if data
                // must be written to HAL
                threadLoop_sleepTime();
                if (mSleepTimeUs == 0) {
                    mCurrentWriteLength = mSinkBufferSize;

                    // Tally underrun frames as we are inserting 0s here.
                    for (const auto& track : activeTracks) {
                        if (track->mFillingUpStatus == Track::FS_ACTIVE) {
                            track->mAudioTrackServerProxy->tallyUnderrunFrames(mNormalFrameCount);
                        }
                    }
                }
            }
            // Either threadLoop_mix() or threadLoop_sleepTime() should have set
            // mMixerBuffer with data if mMixerBufferValid is true and mSleepTimeUs == 0.
            // Merge mMixerBuffer data into mEffectBuffer (if any effects are valid)
            // or mSinkBuffer (if there are no effects).
            //
            // This is done pre-effects computation; if effects change to
            // support higher precision, this needs to move.
            //
            // mMixerBufferValid is only set true by MixerThread::prepareTracks_l().
            // TODO use mSleepTimeUs == 0 as an additional condition.
            if (mMixerBufferValid) {
                void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer;
                audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat;

                // mono blend occurs for mixer threads only (not direct or offloaded)
                // and is handled here if we're going directly to the sink.
                if (requireMonoBlend() && !mEffectBufferValid) {
                    mono_blend(mMixerBuffer, mMixerBufferFormat, mChannelCount, mNormalFrameCount,
                               true /*limit*/);
                }

                if (!hasFastMixer()) {
                    // Balance must take effect after mono conversion.
                    // We do it here if there is no FastMixer.
                    // mBalance detects zero balance within the class for speed (not needed here).
                    mBalance.setBalance(mMasterBalance.load());
                    mBalance.process((float *)mMixerBuffer, mNormalFrameCount);
                }
				// 将数据从mMixerBuffer复制到mSinkBuffer
                memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,
                        mNormalFrameCount * (mChannelCount + mHapticChannelCount));

                // If we're going directly to the sink and there are haptic channels,
                // we should adjust channels as the sample data is partially interleaved
                // in this case.
                if (!mEffectBufferValid && mHapticChannelCount > 0) {
                    adjust_channels_non_destructive(buffer, mChannelCount, buffer,
                            mChannelCount + mHapticChannelCount,
                            audio_bytes_per_sample(format),
                            audio_bytes_per_frame(mChannelCount, format) * mNormalFrameCount);
                }
            }

            mBytesRemaining = mCurrentWriteLength;
            if (isSuspended()) {
                // Simulate write to HAL when suspended (e.g. BT SCO phone call).
                mSleepTimeUs = suspendSleepTimeUs(); // assumes full buffer.
                const size_t framesRemaining = mBytesRemaining / mFrameSize;
                mBytesWritten += mBytesRemaining;
                mFramesWritten += framesRemaining;
                mSuspendedFrames += framesRemaining; // to adjust kernel HAL position
                mBytesRemaining = 0;
            }

            // only process effects if we're going to write
            if (mSleepTimeUs == 0 && mType != OFFLOAD) {
                for (size_t i = 0; i < effectChains.size(); i ++) {
                    effectChains[i]->process_l();
                    // TODO: Write haptic data directly to sink buffer when mixing.
                    if (activeHapticSessionId != AUDIO_SESSION_NONE
                            && activeHapticSessionId == effectChains[i]->sessionId()) {
                        // Haptic data is active in this case, copy it directly from
                        // in buffer to out buffer.
                        const size_t audioBufferSize = mNormalFrameCount
                                * audio_bytes_per_frame(mChannelCount, EFFECT_BUFFER_FORMAT);
                        memcpy_by_audio_format(
                                (uint8_t*)effectChains[i]->outBuffer() + audioBufferSize,
                                EFFECT_BUFFER_FORMAT,
                                (const uint8_t*)effectChains[i]->inBuffer() + audioBufferSize,
                                EFFECT_BUFFER_FORMAT, mNormalFrameCount * mHapticChannelCount);
                    }
                }
            }
        }
        // Process effect chains for offloaded thread even if no audio
        // was read from audio track: process only updates effect state
        // and thus does have to be synchronized with audio writes but may have
        // to be called while waiting for async write callback
        if (mType == OFFLOAD) {
            for (size_t i = 0; i < effectChains.size(); i ++) {
                effectChains[i]->process_l();
            }
        }

        // Only if the Effects buffer is enabled and there is data in the
        // Effects buffer (buffer valid), we need to
        // copy into the sink buffer.
        // TODO use mSleepTimeUs == 0 as an additional condition.
        if (mEffectBufferValid) {
            //ALOGV("writing effect buffer to sink buffer format %#x", mFormat);

            if (requireMonoBlend()) {
                mono_blend(mEffectBuffer, mEffectBufferFormat, mChannelCount, mNormalFrameCount,
                           true /*limit*/);
            }

            if (!hasFastMixer()) {
                // Balance must take effect after mono conversion.
                // We do it here if there is no FastMixer.
                // mBalance detects zero balance within the class for speed (not needed here).
                mBalance.setBalance(mMasterBalance.load());
                mBalance.process((float *)mEffectBuffer, mNormalFrameCount);
            }
			// 将数据从mEffectBuffer拷贝到mSinkBuffer
            memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat,
                    mNormalFrameCount * (mChannelCount + mHapticChannelCount));
            // The sample data is partially interleaved when haptic channels exist,
            // we need to adjust channels here.
            if (mHapticChannelCount > 0) {
                adjust_channels_non_destructive(mSinkBuffer, mChannelCount, mSinkBuffer,
                        mChannelCount + mHapticChannelCount,
                        audio_bytes_per_sample(mFormat),
                        audio_bytes_per_frame(mChannelCount, mFormat) * mNormalFrameCount);
            }
        }

        // enable changes in effect chain
        unlockEffectChains(effectChains);

        if (!waitingAsyncCallback()) {
            // mSleepTimeUs == 0 means we must write to audio hardware
            if (mSleepTimeUs == 0) {
                ssize_t ret = 0;
                // writePeriodNs is updated >= 0 when ret > 0.
                int64_t writePeriodNs = -1;
                if (mBytesRemaining) {
                    // FIXME rewrite to reduce number of system calls
                    const int64_t lastIoBeginNs = systemTime();
					// 将混合后的数据写入硬件
                    ret = threadLoop_write();
                    const int64_t lastIoEndNs = systemTime();
                    if (ret < 0) {
                        mBytesRemaining = 0;
                    } else if (ret > 0) {
                        mBytesWritten += ret;
                        mBytesRemaining -= ret;
                        const int64_t frames = ret / mFrameSize;
                        mFramesWritten += frames;

                        writePeriodNs = lastIoEndNs - mLastIoEndNs;
                        // process information relating to write time.
                        if (audio_has_proportional_frames(mFormat)) {
                            // we are in a continuous mixing cycle
                            if (mMixerStatus == MIXER_TRACKS_READY &&
                                    loopCount == lastLoopCountWritten + 1) {

                                const double jitterMs =
                                        TimestampVerifier<int64_t, int64_t>::computeJitterMs(
                                                {frames, writePeriodNs},
                                                {0, 0} /* lastTimestamp */, mSampleRate);
                                const double processMs =
                                       (lastIoBeginNs - mLastIoEndNs) * 1e-6;

                                Mutex::Autolock _l(mLock);
                                mIoJitterMs.add(jitterMs);
                                mProcessTimeMs.add(processMs);
                            }

                            // write blocked detection
                            const int64_t deltaWriteNs = lastIoEndNs - lastIoBeginNs;
                            if (mType == MIXER && deltaWriteNs > maxPeriod) {
                                mNumDelayedWrites++;
                                if ((lastIoEndNs - lastWarning) > kWarningThrottleNs) {
                                    ATRACE_NAME("underrun");
                                    ALOGW("write blocked for %lld msecs, "
                                            "%d delayed writes, thread %d",
                                            (long long)deltaWriteNs / NANOS_PER_MILLISECOND,
                                            mNumDelayedWrites, mId);
                                    lastWarning = lastIoEndNs;
                                }
                            }
                        }
                        // update timing info.
                        mLastIoBeginNs = lastIoBeginNs;
                        mLastIoEndNs = lastIoEndNs;
                        lastLoopCountWritten = loopCount;
                    }
                } else if ((mMixerStatus == MIXER_DRAIN_TRACK) ||
                        (mMixerStatus == MIXER_DRAIN_ALL)) {
                    threadLoop_drain();
                }
                if (mType == MIXER && !mStandby) {

                    if (mThreadThrottle
                            && mMixerStatus == MIXER_TRACKS_READY // we are mixing (active tracks)
                            && writePeriodNs > 0) {               // we have write period info
                        // Limit MixerThread data processing to no more than twice the
                        // expected processing rate.
                        //
                        // This helps prevent underruns with NuPlayer and other applications
                        // which may set up buffers that are close to the minimum size, or use
                        // deep buffers, and rely on a double-buffering sleep strategy to fill.
                        //
                        // The throttle smooths out sudden large data drains from the device,
                        // e.g. when it comes out of standby, which often causes problems with
                        // (1) mixer threads without a fast mixer (which has its own warm-up)
                        // (2) minimum buffer sized tracks (even if the track is full,
                        //     the app won't fill fast enough to handle the sudden draw).
                        //
                        // Total time spent in last processing cycle equals time spent in
                        // 1. threadLoop_write, as well as time spent in
                        // 2. threadLoop_mix (significant for heavy mixing, especially
                        //                    on low tier processors)

                        // it's OK if deltaMs is an overestimate.

                        const int32_t deltaMs = writePeriodNs / NANOS_PER_MILLISECOND;

                        const int32_t throttleMs = (int32_t)mHalfBufferMs - deltaMs;
                        if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) {
                            usleep(throttleMs * 1000);
                            // notify of throttle start on verbose log
                            ALOGV_IF(mThreadThrottleEndMs == mThreadThrottleTimeMs,
                                    "mixer(%p) throttle begin:"
                                    " ret(%zd) deltaMs(%d) requires sleep %d ms",
                                    this, ret, deltaMs, throttleMs);
                            mThreadThrottleTimeMs += throttleMs;
                            // Throttle must be attributed to the previous mixer loop's write time
                            // to allow back-to-back throttling.
                            // This also ensures proper timing statistics.
                            mLastIoEndNs = systemTime();  // we fetch the write end time again.
                        } else {
                            uint32_t diff = mThreadThrottleTimeMs - mThreadThrottleEndMs;
                            if (diff > 0) {
                                // notify of throttle end on debug log
                                // but prevent spamming for bluetooth
                                ALOGD_IF(!audio_is_a2dp_out_device(outDevice()) &&
                                         !audio_is_hearing_aid_out_device(outDevice()),
                                        "mixer(%p) throttle end: throttle time(%u)", this, diff);
                                mThreadThrottleEndMs = mThreadThrottleTimeMs;
                            }
                        }
                    }
                }

            } else {
                ATRACE_BEGIN("sleep");
                Mutex::Autolock _l(mLock);
                // suspended requires accurate metering of sleep time.
                if (isSuspended()) {
                    // advance by expected sleepTime
                    timeLoopNextNs += microseconds((nsecs_t)mSleepTimeUs);
                    const nsecs_t nowNs = systemTime();

                    // compute expected next time vs current time.
                    // (negative deltas are treated as delays).
                    nsecs_t deltaNs = timeLoopNextNs - nowNs;
                    if (deltaNs < -kMaxNextBufferDelayNs) {
                        // Delays longer than the max allowed trigger a reset.
                        ALOGV("DelayNs: %lld, resetting timeLoopNextNs", (long long) deltaNs);
                        deltaNs = microseconds((nsecs_t)mSleepTimeUs);
                        timeLoopNextNs = nowNs + deltaNs;
                    } else if (deltaNs < 0) {
                        // Delays within the max delay allowed: zero the delta/sleepTime
                        // to help the system catch up in the next iteration(s)
                        ALOGV("DelayNs: %lld, catching-up", (long long) deltaNs);
                        deltaNs = 0;
                    }
                    // update sleep time (which is >= 0)
                    mSleepTimeUs = deltaNs / 1000;
                }
                if (!mSignalPending && mConfigEvents.isEmpty() && !exitPending()) {
                    mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)mSleepTimeUs));
                }
                ATRACE_END();
            }
        }

        // Finally let go of removed track(s), without the lock held
        // since we can't guarantee the destructors won't acquire that
        // same lock.  This will also mutate and push a new fast mixer state.
        threadLoop_removeTracks(tracksToRemove);
        tracksToRemove.clear();

        // FIXME I don't understand the need for this here;
        //       it was in the original code but maybe the
        //       assignment in saveOutputTracks() makes this unnecessary?
        clearOutputTracks();

        // Effect chains will be actually deleted here if they were removed from
        // mEffectChains list during mixing or effects processing
        effectChains.clear();

        // FIXME Note that the above .clear() is no longer necessary since effectChains
        // is now local to this block, but will keep it for now (at least until merge done).
    }

    ............................................................................................................
	
    return false;
}

        所有的传过来的数据会在PlaybackThread的AudioMixer里面进行处理, 创建不同的线程。

AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
        Vector< sp<Track> > *tracksToRemove)
{
    // clean up deleted track ids in AudioMixer before allocating new tracks
    (void)mTracks.processDeletedTrackIds([this](int trackId) {
        // for each trackId, destroy it in the AudioMixer
        if (mAudioMixer->exists(trackId)) {
            mAudioMixer->destroy(trackId);
        }
    });
    mTracks.clearDeletedTrackIds();

    mixer_state mixerStatus = MIXER_IDLE;
    // find out which tracks need to be processed
	// 激活Track的个数
    size_t count = mActiveTracks.size();
	
    ............................................................................................
	
	// 依次查询这些Track的情况
    for (size_t i=0 ; i<count ; i++) {
        const sp<Track> t = mActiveTracks[i];

        // this const just means the local variable doesn't change
        Track* const track = t.get();

        ..............................................................................................

        {   // local variable scope to avoid goto warning
		// 拿到数据控制块
        audio_track_cblk_t* cblk = track->cblk();

        // The first time a track is added we wait
        // for all its buffers to be filled before processing it
        const int trackId = track->id();
		
		..............................................................................................  

        size_t framesReady = track->framesReady();
        
        if ((framesReady >= minFrames) && track->isReady() &&
                !track->isPaused() && !track->isTerminated())
        {
            
			..........................................................................................

            // XXX: these things DON'T need to be done each time
			// 数据准备,将track设置到mAudioMixer
            mAudioMixer->setBufferProvider(trackId, track);
			// 调用enable方法将track的enabled设置为true,然后调用invalidate()方法进行局部刷新
            mAudioMixer->enable(trackId);
			
			// 设置混音器的左右声道音量和音效的水平
            mAudioMixer->setParameter(trackId, param, AudioMixer::VOLUME0, &vlf);
            mAudioMixer->setParameter(trackId, param, AudioMixer::VOLUME1, &vrf);
            mAudioMixer->setParameter(trackId, param, AudioMixer::AUXLEVEL, &vaf);
			//设置音频数据格式
            mAudioMixer->setParameter(
                trackId,
                AudioMixer::TRACK,
                AudioMixer::FORMAT, (void *)track->format());
            mAudioMixer->setParameter(
                trackId,
                AudioMixer::TRACK,
                AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());
            mAudioMixer->setParameter(
                trackId,
                AudioMixer::TRACK,
                AudioMixer::MIXER_CHANNEL_MASK,
                (void *)(uintptr_t)(mChannelMask | mHapticChannelMask));
				
           ........................................................................................

        } else {
            
			.......................................................................................
			
			// 如果处于这4种状态之一,则加入移除队列
            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 HAL
                size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
                int64_t framesWritten = mBytesWritten / mFrameSize;
                if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
                    if (track->isStopped()) {
						// reset会清零读写位置,表示没有可读数据
                        track->reset();
                    }
					//再加入移除队列
                    tracksToRemove->add(track);
                }
            } else {
				// 不处于上面三种状态时,表示暂时没有可读数据,则重试mRetryCount次
                if (--(track->mRetryCount) <= 0) {
                    ALOGI("BUFFER TIMEOUT: remove(%d) from active list on thread %p",
                            trackId, 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 available
                    track->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;
                }
            }
			//禁止这个Track的混音
            mAudioMixer->disable(trackId);
        }

        }   // local variable scope to avoid goto warning

    }
    
	................................................................................................

    return mixerStatus;
}

        prepareTracks_l():参数处理的主要函数

        

        audio_track_cblk控制块:

        AudioTrack与AudioFlinger之间的数据传输分为两种方式,MODE_STATIC与MODE_STREAM
        1. MODE_STATIC: static方式适用于数据较小,实时性比较高的情形,比如ring、系统铃声等。在这种模式下,是在AT端创建共享内存,一次性将数据copy到buffer中,然后传递到AF端。
        2. MODE_STREAM:stream方式适用于数据较大,media播放等更多其他的情况,也比较复杂。在这种模式下,共享内存是由AF创建的,然后通过生产-消费者的模式进行数据的传输。
即AT是数据的生产者,AF是数据的消费者。这个数据读写的控制是由struct audio_track_cblk来实现的

// XXX: these things DON'T need to be done each time
mAudioMixer->setBufferProvider(trackId, track);
mAudioMixer->enable(trackId);

        为每一个准备好的Track设置混音标志,通过mAudioMixer->enable混音

xref: /frameworks/av/media/libaudioprocessing/AudioMixer.cpp

void AudioMixer::enable(int name)
{
    LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
    const std::shared_ptr<Track> &track = mTracks[name];

    if (!track->enabled) {
        track->enabled = true;
        ALOGV("enable(%d)", name);
        invalidate();
    }
}

xref: /frameworks/av/include/media/AudioMixer.h

void invalidate() {
    mHook = &AudioMixer::process__validate;
}

       将mHook设置为AudioMixer::process__validate

xref: /frameworks/av/media/libaudioprocessing/AudioMixer.cpp

void AudioMixer::process__validate()
{
    bool all16BitsStereoNoResample = true;
    bool resampling = false;
    bool volumeRamp = false;

    mEnabled.clear();
    mGroups.clear();
    for (const auto &pair : mTracks) {
        const int name = pair.first;
        const std::shared_ptr<Track> &t = pair.second;
        if (!t->enabled) continue;

        mEnabled.emplace_back(name);  // we add to mEnabled in order of name.
        mGroups[t->mainBuffer].emplace_back(name); // mGroups also in order of name.

        uint32_t n = 0;
        // FIXME can overflow (mask is only 3 bits)
         n |= NEEDS_CHANNEL_1 + t->channelCount - 1;
         if (t->doesResample()) {
             n |= NEEDS_RESAMPLE;
         }
         if (t->auxLevel != 0 && t->auxBuffer != NULL) {
             n |= NEEDS_AUX;
         }
 
         if (t->volumeInc[0]|t->volumeInc[1]) {
             volumeRamp = true;
         } else if (!t->doesResample() && t->volumeRL == 0) {
             n |= NEEDS_MUTE;
         }
         t->needs = n;
 
         if (n & NEEDS_MUTE) {
             t->hook = &Track::track__nop;
         } else {
             if (n & NEEDS_AUX) {
                 all16BitsStereoNoResample = false;
             }
             if (n & NEEDS_RESAMPLE) {
                 all16BitsStereoNoResample = false;
                 resampling = true;
                 t->hook = Track::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
                         t->mMixerInFormat, t->mMixerFormat);
                 ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                         "Track %d needs downmix + resample", name);
             } else {
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
                     t->hook = Track::getTrackHook(
                             (t->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO  // TODO: MONO_HACK
                                     && t->channelMask == AUDIO_CHANNEL_OUT_MONO)
                                 ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
                             t->mMixerChannelCount,
                             t->mMixerInFormat, t->mMixerFormat);
                     all16BitsStereoNoResample = false;
                 }
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
                     t->hook = Track::getTrackHook(TRACKTYPE_NORESAMPLE, t->mMixerChannelCount,
                             t->mMixerInFormat, t->mMixerFormat);
                     ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                             "Track %d needs downmix", name);
                 }
             }
         }
     }
 
     // select the processing hooks
     mHook = &AudioMixer::process__nop;
     if (mEnabled.size() > 0) {
         if (resampling) {
             if (mOutputTemp.get() == nullptr) {
                 mOutputTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
             }
             if (mResampleTemp.get() == nullptr) {
                 mResampleTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
             }
			 // 如果需要重采样,则选择process__genericResampling
             mHook = &AudioMixer::process__genericResampling;
         } else {
             // 不需要重采样
             mHook = &AudioMixer::process__genericNoResampling;
             if (all16BitsStereoNoResample && !volumeRamp) {
                 if (mEnabled.size() == 1) {
                     const std::shared_ptr<Track> &t = mTracks[mEnabled[0]];
                     if ((t->needs & NEEDS_MUTE) == 0) {
                         mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
                                 t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
                     }
                 }
             }
         }
     }
 
	....................................................................................
	
}


....................................................................................



AudioMixer::process_hook_t AudioMixer::getProcessHook(
        int processType, uint32_t channelCount,
        audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
{
    if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
        LOG_ALWAYS_FATAL("bad processType: %d", processType);
        return NULL;
    }
    if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
		// 当只有一路track的时候调用process__oneTrack16BitsStereoNoResampling
        return &AudioMixer::process__oneTrack16BitsStereoNoResampling;
    }
    LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
    switch (mixerInFormat) {
    case AUDIO_FORMAT_PCM_FLOAT:
        switch (mixerOutFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            return &AudioMixer::process__noResampleOneTrack<
                    MIXTYPE_MULTI_SAVEONLY, float /*TO*/, float /*TI*/, TYPE_AUX>;
        case AUDIO_FORMAT_PCM_16_BIT:
            return &AudioMixer::process__noResampleOneTrack<
                    MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, float /*TI*/, TYPE_AUX>;
        default:
            LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
            break;
        }
        break;
    case AUDIO_FORMAT_PCM_16_BIT:
        switch (mixerOutFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            return &AudioMixer::process__noResampleOneTrack<
                    MIXTYPE_MULTI_SAVEONLY, float /*TO*/, int16_t /*TI*/, TYPE_AUX>;
        case AUDIO_FORMAT_PCM_16_BIT:
            return &AudioMixer::process__noResampleOneTrack<
                    MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
        default:
            LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
            break;
        }
        break;
    default:
        LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
        break;
    }
    return NULL;
}

        mHook是总的hook,它根据Track的个数和它的音频数据格式(采样率等)等情况,使用不同的处理函数。

        process__validate:根据Track的格式、数量等信息选择其他的处理函数。
        process__nop:什么都不做。
        process__genericNoResampling:普通无需重采样。
        process__genericResampling:普通需重采样。
        process__OneTrack16BitsStereoNoResampling:一路音频流,双声道,PCM16格式,无需重采样。

        当所有Track准备就绪后,最重要的工作就是混音,混音对象的process就派上了用场。hook最初的值为process__nop,当调用mAudioMixer->enable是能混音后,hook就会指向process__validate。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值