Android音频录制核心:RecordThread::threadLoop解析

RecordThread::threadLoop()是 Android 音频系统(AudioFlinger)中音频录制线程的循环主函数,负责从底层驱动/HAL读取音频数据、管理录音 Track、处理音效链、同步时间戳等。下面我将详细讲解音频数据的处理流程,以及数据的存放地址和相关机制。

函数路径:frameworks/av/services/audioflinger/Threads.cpp

源码:

bool RecordThread::threadLoop()
{
    nsecs_t lastWarning = 0;

    inputStandBy();

reacquire_wakelock:
    {
        audio_utils::lock_guard _l(mutex());
        acquireWakeLock_l();
    }

    // used to request a deferred sleep, to be executed later while mutex is unlocked
    uint32_t sleepUs = 0;

    // timestamp correction enable is determined under lock, used in processing step.
    bool timestampCorrectionEnabled = false;

    int64_t lastLoopCountRead = -2;  // never matches "previous" loop, when loopCount = 0.

    // loop while there is work to do
    for (int64_t loopCount = 0;; ++loopCount) {  // loopCount used for statistics tracking
        // Note: these sp<> are released at the end of the for loop outside of the mutex() lock.
        sp<IAfRecordTrack> activeTrack;
        std::vector<sp<IAfRecordTrack>> oldActiveTracks;
        Vector<sp<IAfEffectChain>> effectChains;

        // activeTracks accumulates a copy of a subset of mActiveTracks
        Vector<sp<IAfRecordTrack>> activeTracks;

        // reference to the (first and only) active fast track
        sp<IAfRecordTrack> fastTrack;

        // reference to a fast track which is about to be removed
        sp<IAfRecordTrack> fastTrackToRemove;

        bool silenceFastCapture = false;

        { // scope for mutex()
            audio_utils::unique_lock _l(mutex());

            processConfigEvents_l();

            // check exitPending here because checkForNewParameters_l() and
            // checkForNewParameters_l() can temporarily release mutex()
            if (exitPending()) {
                break;
            }

            // sleep with mutex unlocked
            if (sleepUs > 0) {
                ATRACE_BEGIN("sleepC");
                (void)mWaitWorkCV.wait_for(_l, std::chrono::microseconds(sleepUs));
                ATRACE_END();
                sleepUs = 0;
                continue;
            }

            // if no active track(s), then standby and release wakelock
            size_t size = mActiveTracks.size();
            if (size == 0) {
                standbyIfNotAlreadyInStandby();
                // exitPending() can't become true here
                releaseWakeLock_l();
                ALOGV("RecordThread: loop stopping");
                // go to sleep
                mWaitWorkCV.wait(_l);
                ALOGV("RecordThread: loop starting");
                goto reacquire_wakelock;
            }

            bool doBroadcast = false;
            bool allStopped = true;
            for (size_t i = 0; i < size; ) {
                if (activeTrack) {  // ensure track release is outside lock.
                    oldActiveTracks.emplace_back(std::move(activeTrack));
                }
                activeTrack = mActiveTracks[i];
                if (activeTrack->isTerminated()) {
                    if (activeTrack->isFastTrack()) {
                        ALOG_ASSERT(fastTrackToRemove == 0);
                        fastTrackToRemove = activeTrack;
                    }
                    removeTrack_l(activeTrack);
                    mActiveTracks.remove(activeTrack);
                    size--;
                    continue;
                }

                IAfTrackBase::track_state activeTrackState = activeTrack->state();
                switch (activeTrackState) {

                case IAfTrackBase::PAUSING:
                    mActiveTracks.remove(activeTrack);
                    activeTrack->setState(IAfTrackBase::PAUSED);
                    if (activeTrack->isFastTrack()) {
                        ALOGV("%s fast track is paused, thus removed from active list", __func__);
                        // Keep a ref on fast track to wait for FastCapture thread to get updated
                        // state before potential track removal
                        fastTrackToRemove = activeTrack;
                    }
                    doBroadcast = true;
                    size--;
                    continue;

                case IAfTrackBase::STARTING_1:
                    sleepUs = 10000;
                    i++;
                    allStopped = false;
                    continue;

                case IAfTrackBase::STARTING_2:
                    doBroadcast = true;
                    if (mStandby) {
                        mThreadMetrics.logBeginInterval();
                        mThreadSnapshot.onBegin();
                        mStandby = false;
                    }
                    activeTrack->setState(IAfTrackBase::ACTIVE);
                    allStopped = false;
                    break;

                case IAfTrackBase::ACTIVE:
                    allStopped = false;
                    break;

                case IAfTrackBase::IDLE:    // cannot be on ActiveTracks if idle
                case IAfTrackBase::PAUSED:  // cannot be on ActiveTracks if paused
                case IAfTrackBase::STOPPED: // cannot be on ActiveTracks if destroyed/terminated
                default:
                    LOG_ALWAYS_FATAL("%s: Unexpected active track state:%d, id:%d, tracks:%zu",
                            __func__, activeTrackState, activeTrack->id(), size);
                }

                if (activeTrack->isFastTrack()) {
                    ALOG_ASSERT(!mFastTrackAvail);
                    ALOG_ASSERT(fastTrack == 0);
                    // if the active fast track is silenced either:
                    // 1) silence the whole capture from fast capture buffer if this is
                    //    the only active track
                    // 2) invalidate this track: this will cause the client to reconnect and possibly
                    //    be invalidated again until unsilenced
                    bool invalidate = false;
                    if (activeTrack->isSilenced()) {
                        if (size > 1) {
                            invalidate = true;
                        } else {
                            silenceFastCapture = true;
                        }
                    }
                    // Invalidate fast tracks if access to audio history is required as this is not
                    // possible with fast tracks. Once the fast track has been invalidated, no new
                    // fast track will be created until mMaxSharedAudioHistoryMs is cleared.
                    if (mMaxSharedAudioHistoryMs != 0) {
                        invalidate = true;
                    }
                    if (invalidate) {
                        activeTrack->invalidate();
                        fastTrackToRemove = activeTrack;
                        removeTrack_l(activeTrack);
                        mActiveTracks.remove(activeTrack);
                        size--;
                        continue;
                    }
                    fastTrack = activeTrack;
                }

                activeTracks.add(activeTrack);
                i++;

            }

            mActiveTracks.updatePowerState_l(this);

            // check if traces have been enabled.
            bool atraceEnabled = ATRACE_ENABLED();
            if (atraceEnabled != mAtraceEnabled) [[unlikely]] {
                mAtraceEnabled = atraceEnabled;
                if (atraceEnabled) {
                    const auto devices = patchSourcesToString(&mPatch);
                    for (const auto& track : activeTracks) {
                        track->logRefreshInterval(devices);
                    }
                }
            }

            updateMetadata_l();

            if (allStopped) {
                standbyIfNotAlreadyInStandby();
            }
            if (doBroadcast) {
                mStartStopCV.notify_all();
            }

            // sleep if there are no active tracks to process
            if (activeTracks.isEmpty()) {
                if (sleepUs == 0) {
                    sleepUs = kRecordThreadSleepUs;
                }
                continue;
            }
            sleepUs = 0;

            timestampCorrectionEnabled = isTimestampCorrectionEnabled_l();
            lockEffectChains_l(effectChains);
            // We're exiting locked scope with non empty activeTracks, make sure
            // that we're not in standby mode which we could have entered if some
            // tracks were muted/unmuted.
            mStandby = false;
        }

        // thread mutex is now unlocked, mActiveTracks unknown, activeTracks.size() > 0

        size_t size = effectChains.size();
        for (size_t i = 0; i < size; i++) {
            // thread mutex is not locked, but effect chain is locked
            effectChains[i]->process_l();
        }

        // Push a new fast capture state if fast capture is not already running, or cblk change
        if (mFastCapture != 0) {
            FastCaptureStateQueue *sq = mFastCapture->sq();
            FastCaptureState *state = sq->begin();
            bool didModify = false;
            FastCaptureStateQueue::block_t block = FastCaptureStateQueue::BLOCK_UNTIL_PUSHED;
            if (state->mCommand != FastCaptureState::READ_WRITE /* FIXME &&
                    (kUseFastMixer != FastMixer_Dynamic || state->mTrackMask > 1)*/) {
                if (state->mCommand == FastCaptureState::COLD_IDLE) {
                    int32_t old = android_atomic_inc(&mFastCaptureFutex);
                    if (old == -1) {
                        (void) syscall(__NR_futex, &mFastCaptureFutex, FUTEX_WAKE_PRIVATE, 1);
                    }
                }
                state->mCommand = FastCaptureState::READ_WRITE;
#if 0   // FIXME
                mFastCaptureDumpState.increaseSamplingN(mAfThreadCallback->isLowRamDevice() ?
                        FastThreadDumpState::kSamplingNforLowRamDevice :
                        FastThreadDumpState::kSamplingN);
#endif
                didModify = true;
            }
            audio_track_cblk_t *cblkOld = state->mCblk;
            audio_track_cblk_t *cblkNew = fastTrack != 0 ? fastTrack->cblk() : NULL;
            if (cblkNew != cblkOld) {
                state->mCblk = cblkNew;
                // block until acked if removing a fast track
                if (cblkOld != NULL) {
                    block = FastCaptureStateQueue::BLOCK_UNTIL_ACKED;
                }
                didModify = true;
            }
            AudioBufferProvider* abp = (fastTrack != 0 && fastTrack->isPatchTrack()) ?
                    reinterpret_cast<AudioBufferProvider*>(fastTrack.get()) : nullptr;
            if (state->mFastPatchRecordBufferProvider != abp) {
                state->mFastPatchRecordBufferProvider = abp;
                state->mFastPatchRecordFormat = fastTrack == 0 ?
                        AUDIO_FORMAT_INVALID : fastTrack->format();
                didModify = true;
            }
            if (state->mSilenceCapture != silenceFastCapture) {
                state->mSilenceCapture = silenceFastCapture;
                didModify = true;
            }
            sq->end(didModify);
            if (didModify) {
                sq->push(block);
#if 0
                if (kUseFastCapture == FastCapture_Dynamic) {
                    mNormalSource = mPipeSource;
                }
#endif
            }
        }

        // now run the fast track destructor with thread mutex unlocked
        fastTrackToRemove.clear();

        // Read from HAL to keep up with fastest client if multiple active tracks, not slowest one.
        // Only the client(s) that are too slow will overrun. But if even the fastest client is too
        // slow, then this RecordThread will overrun by not calling HAL read often enough.
        // If destination is non-contiguous, first read past the nominal end of buffer, then
        // copy to the right place.  Permitted because mRsmpInBuffer was over-allocated.

        int32_t rear = mRsmpInRear & (mRsmpInFramesP2 - 1);
        ssize_t framesRead = 0; // not needed, remove clang-tidy warning.
        const int64_t lastIoBeginNs = systemTime(); // start IO timing

        // If an NBAIO source is present, use it to read the normal capture's data
        if (mPipeSource != 0) {
            size_t framesToRead = min(mRsmpInFramesOA - rear, mRsmpInFramesP2 / 2);

            // The audio fifo read() returns OVERRUN on overflow, and advances the read pointer
            // to the full buffer point (clearing the overflow condition).  Upon OVERRUN error,
            // we immediately retry the read() to get data and prevent another overflow.
            for (int retries = 0; retries <= 2; ++retries) {
                ALOGW_IF(retries > 0, "overrun on read from pipe, retry #%d", retries);
                framesRead = mPipeSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize,
                        framesToRead);
                if (framesRead != OVERRUN) break;
            }

            const ssize_t availableToRead = mPipeSource->availableToRead();
            if (availableToRead >= 0) {
                mMonopipePipeDepthStats.add(availableToRead);
                // PipeSource is the primary clock.  It is up to the AudioRecord client to keep up.
                LOG_ALWAYS_FATAL_IF((size_t)availableToRead > mPipeFramesP2,
                        "more frames to read than fifo size, %zd > %zu",
                        availableToRead, mPipeFramesP2);
                const size_t pipeFramesFree = mPipeFramesP2 - availableToRead;
                const size_t sleepFrames = min(pipeFramesFree, mRsmpInFramesP2) / 2;
                ALOGVV("mPipeFramesP2:%zu mRsmpInFramesP2:%zu sleepFrames:%zu availableToRead:%zd",
                        mPipeFramesP2, mRsmpInFramesP2, sleepFrames, availableToRead);
                sleepUs = (sleepFrames * 1000000LL) / mSampleRate;
            }
            if (framesRead < 0) {
                status_t status = (status_t) framesRead;
                switch (status) {
                case OVERRUN:
                    ALOGW("overrun on read from pipe");
                    framesRead = 0;
                    break;
                case NEGOTIATE:
                    ALOGE("re-negotiation is needed");
                    framesRead = -1;  // Will cause an attempt to recover.
                    break;
                default:
                    ALOGE("unknown error %d on read from pipe", status);
                    break;
                }
            }
        // otherwise use the HAL / AudioStreamIn directly
        } else {
            ATRACE_BEGIN("read");
            size_t bytesRead;
            status_t result = mSource->read(
                    (uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize, &bytesRead);
            ATRACE_END();
            if (result < 0) {
                framesRead = result;
            } else {
                framesRead = bytesRead / mFrameSize;
            }
        }

        const int64_t lastIoEndNs = systemTime(); // end IO timing

        // Update server timestamp with server stats
        // systemTime() is optional if the hardware supports timestamps.
        if (framesRead >= 0) {
            mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] += framesRead;
            mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = lastIoEndNs;
        }

        // Update server timestamp with kernel stats
        if (mPipeSource.get() == nullptr /* don't obtain for FastCapture, could block */) {
            int64_t position, time;
            if (mStandby) {
                mTimestampVerifier.discontinuity(audio_is_linear_pcm(mFormat) ?
                    mTimestampVerifier.DISCONTINUITY_MODE_CONTINUOUS :
                    mTimestampVerifier.DISCONTINUITY_MODE_ZERO);
            } else if (mSource->getCapturePosition(&position, &time) == NO_ERROR
                    && time > mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]) {

                mTimestampVerifier.add(position, time, mSampleRate);
                if (timestampCorrectionEnabled) {
                    ALOGVV("TS_BEFORE: %d %lld %lld",
                            id(), (long long)time, (long long)position);
                    auto correctedTimestamp = mTimestampVerifier.getLastCorrectedTimestamp();
                    position = correctedTimestamp.mFrames;
                    time = correctedTimestamp.mTimeNs;
                    ALOGVV("TS_AFTER: %d %lld %lld",
                            id(), (long long)time, (long long)position);
                }

                mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = position;
                mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = time;
                // Note: In general record buffers should tend to be empty in
                // a properly running pipeline.
                //
                // Also, it is not advantageous to call get_presentation_position during the read
                // as the read obtains a lock, preventing the timestamp call from executing.
            } else {
                mTimestampVerifier.error();
            }
        }

        // From the timestamp, input read latency is negative output write latency.
        const audio_input_flags_t flags = mInput != NULL ? mInput->flags : AUDIO_INPUT_FLAG_NONE;
        const double latencyMs = IAfRecordTrack::checkServerLatencySupported(mFormat, flags)
                ? - mTimestamp.getOutputServerLatencyMs(mSampleRate) : 0.;
        if (latencyMs != 0.) { // note 0. means timestamp is empty.
            mLatencyMs.add(latencyMs);
        }

        // Use this to track timestamp information
        // ALOGD("%s", mTimestamp.toString().c_str());

        if (framesRead < 0 || (framesRead == 0 && mPipeSource == 0)) {
            ALOGE("read failed: framesRead=%zd", framesRead);
            // Force input into standby so that it tries to recover at next read attempt
            inputStandBy();
            sleepUs = kRecordThreadSleepUs;
        }
        if (framesRead <= 0) {
            goto unlock;
        }
        ALOG_ASSERT(framesRead > 0);
        mFramesRead += framesRead;

#ifdef TEE_SINK
        (void)mTee.write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead);
#endif
        // If destination is non-contiguous, we now correct for reading past end of buffer.
        {
            size_t part1 = mRsmpInFramesP2 - rear;
            if ((size_t) framesRead > part1) {
                memcpy(mRsmpInBuffer, (uint8_t*)mRsmpInBuffer + mRsmpInFramesP2 * mFrameSize,
                        (framesRead - part1) * mFrameSize);
            }
        }
        mRsmpInRear = audio_utils::safe_add_overflow(mRsmpInRear, (int32_t)framesRead);

        size = activeTracks.size();

        // loop over each active track
        for (size_t i = 0; i < size; i++) {
            if (activeTrack) {  // ensure track release is outside lock.
                oldActiveTracks.emplace_back(std::move(activeTrack));
            }
            activeTrack = activeTracks[i];

            // skip fast tracks, as those are handled directly by FastCapture
            if (activeTrack->isFastTrack()) {
                continue;
            }

            // TODO: This code probably should be moved to RecordTrack.
            // TODO: Update the activeTrack buffer converter in case of reconfigure.

            enum {
                OVERRUN_UNKNOWN,
                OVERRUN_TRUE,
                OVERRUN_FALSE
            } overrun = OVERRUN_UNKNOWN;

            // loop over getNextBuffer to handle circular sink
            for (;;) {

                activeTrack->sinkBuffer().frameCount = ~0;
                status_t status = activeTrack->getNextBuffer(&activeTrack->sinkBuffer());
                size_t framesOut = activeTrack->sinkBuffer().frameCount;
                LOG_ALWAYS_FATAL_IF((status == OK) != (framesOut > 0));

                // check available frames and handle overrun conditions
                // if the record track isn't draining fast enough.
                bool hasOverrun;
                size_t framesIn;
                activeTrack->resamplerBufferProvider()->sync(&framesIn, &hasOverrun);
                if (hasOverrun) {
                    overrun = OVERRUN_TRUE;
                }
                if (framesOut == 0 || framesIn == 0) {
                    break;
                }

                // Don't allow framesOut to be larger than what is possible with resampling
                // from framesIn.
                // This isn't strictly necessary but helps limit buffer resizing in
                // RecordBufferConverter.  TODO: remove when no longer needed.
                if (audio_is_linear_pcm(activeTrack->format())) {
                    framesOut = min(framesOut,
                            destinationFramesPossible(
                                    framesIn, mSampleRate, activeTrack->sampleRate()));
                }

                if (activeTrack->isDirect()) {
                    // No RecordBufferConverter used for direct streams. Pass
                    // straight from RecordThread buffer to RecordTrack buffer.
                    AudioBufferProvider::Buffer buffer;
                    buffer.frameCount = framesOut;
                    const status_t getNextBufferStatus =
                            activeTrack->resamplerBufferProvider()->getNextBuffer(&buffer);
                    if (getNextBufferStatus == OK && buffer.frameCount != 0) {
                        ALOGV_IF(buffer.frameCount != framesOut,
                                "%s() read less than expected (%zu vs %zu)",
                                __func__, buffer.frameCount, framesOut);
                        framesOut = buffer.frameCount;
                        memcpy(activeTrack->sinkBuffer().raw,
                                buffer.raw, buffer.frameCount * mFrameSize);
                        activeTrack->resamplerBufferProvider()->releaseBuffer(&buffer);
                    } else {
                        framesOut = 0;
                        ALOGE("%s() cannot fill request, status: %d, frameCount: %zu",
                            __func__, getNextBufferStatus, buffer.frameCount);
                    }
                } else {
                    // process frames from the RecordThread buffer provider to the RecordTrack
                    // buffer
                    framesOut = activeTrack->recordBufferConverter()->convert(
                            activeTrack->sinkBuffer().raw,
                            activeTrack->resamplerBufferProvider(),
                            framesOut);
                }

                if (framesOut > 0 && (overrun == OVERRUN_UNKNOWN)) {
                    overrun = OVERRUN_FALSE;
                }

                // MediaSyncEvent handling: Synchronize AudioRecord to AudioTrack completion.
                const ssize_t framesToDrop =
                        activeTrack->synchronizedRecordState().updateRecordFrames(framesOut);
                if (framesToDrop == 0) {
                    // no sync event, process normally, otherwise ignore.
                    if (framesOut > 0) {
                        activeTrack->sinkBuffer().frameCount = framesOut;
                        // Sanitize before releasing if the track has no access to the source data
                        // An idle UID receives silence from non virtual devices until active
                        if (activeTrack->isSilenced()) {
                            memset(activeTrack->sinkBuffer().raw,
                                    0, framesOut * activeTrack->frameSize());
                        }
                        activeTrack->releaseBuffer(&activeTrack->sinkBuffer());
                    }
                }
                if (framesOut == 0) {
                    break;
                }
            }

            switch (overrun) {
            case OVERRUN_TRUE:
                // client isn't retrieving buffers fast enough
                if (!activeTrack->setOverflow()) {
                    nsecs_t now = systemTime();
                    // FIXME should lastWarning per track?
                    if ((now - lastWarning) > kWarningThrottleNs) {
                        ALOGW("RecordThread: buffer overflow");
                        lastWarning = now;
                    }
                }
                break;
            case OVERRUN_FALSE:
                activeTrack->clearOverflow();
                break;
            case OVERRUN_UNKNOWN:
                break;
            }

            // update frame information and push timestamp out
            activeTrack->updateTrackFrameInfo(
                    activeTrack->serverProxy()->framesReleased(),
                    mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER],
                    mSampleRate, mTimestamp);
        }

unlock:
        // enable changes in effect chain
        unlockEffectChains(effectChains);
        // effectChains doesn't need to be cleared, since it is cleared by destructor at scope end
        if (audio_has_proportional_frames(mFormat)
            && loopCount == lastLoopCountRead + 1) {
            const int64_t readPeriodNs = lastIoEndNs - mLastIoEndNs;
            const double jitterMs =
                TimestampVerifier<int64_t, int64_t>::computeJitterMs(
                    {framesRead, readPeriodNs},
                    {0, 0} /* lastTimestamp */, mSampleRate);
            const double processMs = (lastIoBeginNs - mLastIoEndNs) * 1e-6;

            audio_utils::lock_guard _l(mutex());
            mIoJitterMs.add(jitterMs);
            mProcessTimeMs.add(processMs);
        }
       mThreadloopExecutor.process();
        // update timing info.
        mLastIoBeginNs = lastIoBeginNs;
        mLastIoEndNs = lastIoEndNs;
        lastLoopCountRead = loopCount;
    }
    mThreadloopExecutor.process(); // process any remaining deferred actions.
    // deferred actions after this point are ignored.

    standbyIfNotAlreadyInStandby();

    {
        audio_utils::lock_guard _l(mutex());
        for (size_t i = 0; i < mTracks.size(); i++) {
            sp<IAfRecordTrack> track = mTracks[i];
            track->invalidate();
        }
        mActiveTracks.clear();
        mStartStopCV.notify_all();
    }

    releaseWakeLock();

    ALOGV("RecordThread %p exiting", this);
    return false;
}


一. 整体流程简介


大致流程如下:
1. 线程初始化、进入 Standby。
2. 检查活跃录音 Track 列表(mActiveTracks),如果无活动则休眠,否则进入后续处理。
3. 检查和更新活跃 Track 的状态(Active、Pausing、Paused、Starting 等)。
4. 处理 Effect Chain(音效链),对音频数据做处理。
5. 从底层音频输入源(HAL/pipe)读取音频数据,存放到线程内部缓冲区。
6. 将数据分发到各个活跃的 RecordTrack,并进行采样率转换、格式转换、静音处理、同步处理等。
7. 跟踪和更新时间戳信息。
8. 处理 buffer overrun(溢出)等异常情况。
9. 线程退出前,清理资源和状态。

二. 音频数据处理


 2.1. HAL/pipe 数据读取与存储


缓冲区 mRsmpInBuffer
定义:`mRsmpInBuffer` 是 RecordThread 内部用于存放从底层读取到的原始音频数据的缓冲区。
类型:通常是 `void*` 或 `uint8_t*`,分配大小为若干帧(mRsmpInFramesP2,通常为 2 的幂),每帧大小为 mFrameSize(字节)。
用途:所有从 HAL/pipe 读取到的数据,首先写入这个环形缓冲区。这个缓冲区有自己的“rear”指针 mRsmpInRear,用于追踪写入的位置。

数据读取
如果使用 FastCapture,则数据从 pipe 读取(`mPipeSource->read()`),否则直接调用 HAL(`mSource->read()`)。
读取到的数据写入 mRsmpInBuffer 的相应位置(rear * mFrameSize)。
读取的字节数/帧数保存在 framesRead。

framesRead = mPipeSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesToRead);
// 或
status_t result = mSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize, &bytesRead);
framesRead = bytesRead / mFrameSize;


mRsmpInRear 会相应地自增,始终指向最新数据的尾部。


2.2. Track 数据分发与转换


活跃 Track(activeTracks)每个代表一个客户端(如一个 AudioRecord),它们从 RecordThread 获取音频数据。


Track 的缓冲区

每个 Track 有自己的缓冲区(通常通过 AudioTrackServerProxy/RecordTrackBuffer 组织),这块缓冲区用于与应用层共享数据。
线程通过 activeTrack->sinkBuffer().raw 指针拿到可写区域。

数据分发流程
1. getNextBuffer:获取 Track 可写缓冲区(sinkBuffer)。
2. 数据准备:如果是 Direct Track(直通),直接拷贝;否则通过 RecordBufferConverter 做采样率/格式转换。
3. 数据写入:将 mRsmpInBuffer 区域的数据转换后写入 sinkBuffer().raw。
4. releaseBuffer:通知 Track 数据已写好,应用层可以读取。

非直通流

framesOut = activeTrack->recordBufferConverter()->convert(
        activeTrack->sinkBuffer().raw,
        activeTrack->resamplerBufferProvider(),
        framesOut);


RecordBufferConverter负责:

若 Track 采样率/格式与 HAL 不同,进行重采样/重格式化。
若需要静音,填充 0。
若需要同步,可能跳过部分帧。
 

2.3. 总结数据流向


1. 数据起点:底层驱动/HAL -> 通过 mSource->read() 或 mPipeSource->read()。
2. 线程缓冲区:写入 RecordThread 的 mRsmpInBuffer(环形缓冲区,由 mRsmpInRear 指针维护读写位置)。
3. Track 分发:线程循环遍历所有活跃 Track,把数据(经必要转换后)写入各自 Track 的共享缓冲区 sinkBuffer().raw。
4. 应用层读取:应用层的 AudioRecord 从 Track 的共享缓冲区继续读取数据。

三. 关键结构与成员说明


mRsmpInBuffer:RecordThread 的输入音频环形缓冲区,存放底层读取的原始音频数据。
mRsmpInRear:指向 mRsmpInBuffer 中最新写入数据的尾部帧索引。
mActiveTracks:当前需要分发音频数据的 Track 列表。
activeTrack->sinkBuffer().raw:每个 Track 供应用层读取的音频数据缓冲区。
RecordBufferConverter:负责从 mRsmpInBuffer 取数据,并转换成 Track 所需格式后写入 sinkBuffer().raw。
mFrameSize:每帧字节数(= 通道数 * 采样位宽 / 8)。
mBufferSize:每次从 HAL 读取的字节数。
 

四. 总结


音频数据的处理和存放地址核心流程:

读取:底层 HAL/pipe -> mRsmpInBuffer(环形缓冲区)。

分发/转换:mRsmpInBuffer -> RecordBufferConverter -> 各 Track 的 sinkBuffer().raw。

应用层读取:AudioRecord 通过 Track 的 buffer 读取数据。

整个过程确保了多 Track 并发录音、格式灵活转换、高效内存复用、异常溢出处理,以及音效链和同步等附加功能。

五.补充


在 Android AudioFlinger 的录音通路(RecordThread)中,确实存在两个层级的环形缓冲区,分别承担不同的数据流转和功能。
1. 第一个环形缓冲区(HAL读取原始数据缓冲区)
RecordThread::mRsmpInBuffer
配套指针/索引:mRsmpInRear、mRsmpInFramesP2 等

作用:
存放从 HAL(或 FastCapture 管道)读取到的原始音频数据。
这是线程级共享的缓冲区,所有活跃 Track(录音客户端)都从这里获取新数据。
是一个环形缓冲区(ring buffer/circular buffer),利用 rear 指针循环写入。

举例

mSource->read((uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize, &bytesRead);


2. 第二个环形缓冲区(Track应用读取缓冲区)
每个 RecordTrack(活跃Track)都有自己的 buffer
一般是通过RecordTrack::mBuffer 或 serverProxy 管理
应用 AudioRecord 通过 mmap 或 binder 与这个 buffer 通信

作用:
存放已经分发并转换好的数据,供应用层(AudioRecord)读取。
是Track级别的缓冲区,每个活跃Track(即每个录音客户端)拥有独立 buffer。
也是环形缓冲区(ring buffer/circular buffer),通常由 Track 的 serverProxy 管理读写指针。

 举例

// RecordThread往track缓冲区写(sinkBuffer().raw)
memcpy(activeTrack->sinkBuffer().raw, src, framesOut * activeTrack->frameSize());
// 应用AudioRecord从track缓冲区读
AudioRecord::obtainBuffer() // 读track buffer


3. 数据流转关系
1. HAL/pipe 读取数据 ➔ RecordThread::mRsmpInBuffer
2. RecordThread循环分发(可做采样率/格式转换) ➔ 各 Track 的 buffer(sinkBuffer().raw)
3. 应用 AudioRecord 读取 ➔ Track buffer

这样设计的好处是:
解耦:多个Track可以并发读取同一份原始数据,互不干扰。
灵活性:每个Track可以有不同的采样率、格式、通道数,RecordThread分发时完成转换。
效率高:避免重复从HAL读取数据,多Track共享原始数据。
防止阻塞:每个Track自己的环形缓冲区,应用读取慢不会影响其他Track。

当遇到录音声音异常问题,要排查问题出现的哪个阶段,可以通过Android自带的TeeSink功能dump各阶段音频流的原始PCM音频数据进行分析。

下一篇Android 音频录制核心:ServerProxy::obtainBuffer揭秘-优快云博客

讲解如何拿到第二个环形缓冲区的buffer的核心代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值