audiotrack的write流程(mode_stream)

简述

到目前为止,我们已经完成了audiotrack创建和设置,它完成了cblk、clientproxy、serverproxy、track、output等的创建和关联,并且打开了音频通路,紧接着就等我们写数据了。

读写数据的操作是一个生产者和消费者的逻辑。这边的逻辑是这样的:

生产者写数据,线程消费数据,线程消费了n个数据之后,发现已经完成minbuffersize的消费,会去通知你继续填充数据。它们是在两个独立的线程里面的。

所以可能会出现三种情况的数据填写:

  1. 生产者极快。生产者数据写完了,等待消费者消费到minbuffersize,然后唤醒生产者继续填充,在消费者进行下次消费之前已经填充完,又进入等待。看起来好像是有序的填充,等待,消费,唤醒,填充,等待,消费,唤醒的线性逻辑
  2. 生产和消费速度相当。生产者数据写完了,等待消费者消费到minbuffersize,然后唤醒生产者继续填充,生产者的生产速度极慢,每次填充都正好满足消费者的。这边看起来就是异步的了,一边一直在填充、一边一直在消费,基本上没有唤醒情况
  3. 生产速度远远小于消费速度。生产者的数据写入太慢,消费者消费过快,这个时候就会出现underrun了,这个时候消费者会给生产者一定的机会,如果还是没能够给到数据,则就将其赶出判断的行列。

正文

生产者部分

从java开始看,我们平常调用的不指定是否阻塞,默认就是阻塞的。

public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
    return write(audioData, offsetInShorts, sizeInShorts, WRITE_BLOCKING);
}
// 这边有个需要注意的是,传入的数据所存放的buffer是类型的,比如byte的话,则format不可以为float类型
public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
        @WriteMode int writeMode) {
    // 必须已经经过初始化了,并且对format有限制,因为传入的数组是一个byte类型
    if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
        return ERROR_INVALID_OPERATION;
    }
    // writeMode只有两种情况,一种是blocking,另外一种是non blocking
    if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) {
        return ERROR_BAD_VALUE;
    }

    if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
            || (offsetInBytes + sizeInBytes < 0)    // detect integer overflow
            || (offsetInBytes + sizeInBytes > audioData.length)) {
        return ERROR_BAD_VALUE;
    }

    int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
            writeMode == WRITE_BLOCKING);

    if ((mDataLoadMode == MODE_STATIC)
            && (mState == STATE_NO_STATIC_DATA)
            && (ret > 0)) {
        // benign race with respect to other APIs that read mState
        mState = STATE_INITIALIZED;
    }

    return ret;
}

上面函数做了下常规检查,然后就调用native的函数进行执行写的操作了。

看下函数实现native_write_byte

static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
        jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
        jint javaAudioFormat, jboolean isWriteBlocking) {

    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    if (lpTrack == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
                "Unable to retrieve AudioTrack pointer for write()");
        return (jint)AUDIO_JAVA_INVALID_OPERATION;
    }

    ScopedBytesRO bytes(env, javaBytes);
    if (bytes.get() == NULL) {
        return (jint)AUDIO_JAVA_BAD_VALUE;
    }
    // 调用writeToTrack执行写的操作
    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
            sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);

    return written;
}

template <typename T>
static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
                         jint offsetInSamples, jint sizeInSamples, bool blocking) {
    // give the data to the native AudioTrack object (the data starts at the offset)
    ssize_t written = 0;
    // regular write() or copy the data to the AudioTrack's shared memory?
    size_t sizeInBytes = sizeInSamples * sizeof(T);
    // 如果非share memory的话,直接调用audiotrack的write函数
    if (track->sharedBuffer() == 0) {
        written = track->write(data + offsetInSamples, sizeInBytes, blocking);
        // for compatibility with earlier behavior of write(), return 0 in this case
        if (written == (ssize_t) WOULD_BLOCK) {
            written = 0;
        }
    } else {
        // 写到share memory,检查下容量
        // writing to shared memory, check for capacity
        if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
            sizeInBytes = track->sharedBuffer()->size();
        }
        // 直接进行拷贝
        memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
        written = sizeInBytes;
    }
    if (written > 0) {
        return written / sizeof(T);
    }
    // for compatibility, error codes pass through unchanged
    return written;
}

上面的函数只是简单进行write的操作,我们现在进入主体函数。

ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{
    // 该write函数要求mTransfer必须为TRANSFER_SYNC并且不是timetrack
    if (mTransfer != TRANSFER_SYNC || mIsTimed) {
        return INVALID_OPERATION;
    }
    // 判断下flag中是否带有direct flag,如果是flag,则去掉CBLK_UNDERRUN、CBLK_LOOP_CYCLE
    // CBLK_LOOP_FINAL、CBLK_BUFFER_END
    if (isDirect()) {
        AutoMutex lock(mLock);
        int32_t flags = android_atomic_and(
                            ~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END),
                            &mCblk->mFlags);
        // 如果此时flag还含有CBLK_INVALID flag
        if (flags & CBLK_INVALID) {
            return DEAD_OBJECT;
        }
    }

    if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
        // Sanity-check: user is most-likely passing an error code, and it would
        // make the return value ambiguous (actualSize vs error).
        return BAD_VALUE;
    }

    size_t written = 0;
    Buffer audioBuffer;
    // 进入循环写,直到userSize写完
    while (userSize >= mFrameSize) {
        // 计算下用户写入的数据 含有多少帧
        audioBuffer.frameCount = userSize / mFrameSize;
        // 获取buffer空间,blocking默认为true,所以选择ClientProxy::kForever
        status_t err = obtainBuffer(&audioBuffer,
                blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
        // 获取失败
        if (err < 0) {
            // 如果之前已经有写入一定的数据,则跳出,否则返回错误的
            if (written > 0) {
                break;
            }
            return ssize_t(err);
        }
        // audioBuffer.size记录的是获取到的空间的大小,然后进行数据拷贝,变量更新
        size_t toWrite = audioBuffer.size;
        memcpy(audioBuffer.i8, buffer, toWrite);
        buffer = ((const char *) buffer) + toWrite;
        userSize -= toWrite;
        written += toWrite;
        // 更新下buffer的状态
        releaseBuffer(&audioBuffer);
    }

    return written;
}

write函数主要做了:

  1. 判断这个track是否可以写
  2. 对于direct的track,进行一些flag的消除
  3. 开始写,主动去获取写的空间,拷贝数据,更新索引,更新buffer状态,检测是否已经还有数据,如果有继续写,没有的话则返回这次写入的数据长度。

这个函数里面有两个重要的函数我们要进一步分析的,一个是obtainBuffer,另一个是releaseBuffer.

先来看obtainBuffer

status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
        struct timespec *elapsed, size_t *nonContig)
{
    // previous and new IAudioTrack sequence numbers are used to detect track re-creation
    // 之前和新的sequence用来检测track是否re-creation
    uint32_t oldSequence = 0;
    uint32_t newSequence;

    Proxy::Buffer buffer;
    status_t status = NO_ERROR;

    static const int32_t kMaxTries = 5;
    int32_t tryCounter = kMaxTries;
    // 循环的开始
    do {
        // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to
        // keep them from going away if another thread re-creates the track during obtainBuffer()
        // obtainBuffer函数调用必须unlock掉mutex,在此之前有一些参数需要
        sp<AudioTrackClientProxy> proxy;
        sp<IMemory> iMem;

        {   // start of lock scope
            AutoMutex lock(mLock);

            newSequence = mSequence;
            // 是否上一次obtainBuffer失败是由于media server挂掉或者自动放弃治疗?
            if (status == DEAD_OBJECT) {
                // re-create track,除非有人已经做了。
                if (newSequence == oldSequence) {
                    // 会重新createtrack,里面会更新sequence的值
                    status = restoreTrack_l("obtainBuffer");
                    // 一旦restore失败了,则直接跳出来。
                    if (status != NO_ERROR) {
                        buffer.mFrameCount = 0;
                        buffer.mRaw = NULL;
                        buffer.mNonContig = 0;
                        break;
                    }
                }
            }
            // 更新下sequence
            oldSequence = newSequence;

            // Keep the extra references
            // 持有这些额外的引用
            proxy = mProxy;
            iMem = mCblkMemory;
            // 中途切换成STATE_STOPPING
            if (mState == STATE_STOPPING) {
                status = -EINTR;
                buffer.mFrameCount = 0;
                buffer.mRaw = NULL;
                buffer.mNonContig = 0;
                break;
            }

            // 如果状态为stoped或者paused的话,将请求设置为non-blocking
            if (mState != STATE_ACTIVE) {
                requested = &ClientProxy::kNonBlocking;
            }

        }   // end of lock scope
        // 重新组装buffer,传给proxy去获取buffer
        buffer.mFrameCount = audioBuffer->frameCount;
        // FIXME starts the requested timeout and elapsed over from scratch
        status = proxy->obtainBuffer(&buffer, requested, elapsed);
      // 如果status为DEAD的话,则会等待5次机会,每次都会restore。
    } while ((status == DEAD_OBJECT) && (tryCounter-- > 0));
    // 把获取到的空间数据进行更新到传进来的结构中。
    audioBuffer->frameCount = buffer.mFrameCount;
    audioBuffer->size = buffer.mFrameCount * mFrameSize;
    audioBuffer->raw = buffer.mRaw;
    if (nonContig != NULL) {
        *nonContig = buffer.mNonContig;
    }
    return status;
}

上面的函数主要实现了:

  1. 判断IAudioTrack有没有挂掉,如果挂掉的话,进行重新创建
  2. 一些对象的引用,不知道目的何在
  3. 如果状态切换为STOPPING,则直接退出
  4. 如果状态不为ACTIVE,则将请求设置为非阻塞
  5. 通过proxy去获取buffer
  6. 如果发现track挂掉了,并且次数还没用完则再次循环。
  7. 封装数据,返回状态。

接下来我们看proxy调用obtainBuffer函数

status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
        struct timespec *elapsed)
{
    LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0);
    struct timespec total;          // 记录等待所花掉的时间,从当你填满等server消费,通知的时间段
    total.tv_sec = 0;
    total.tv_nsec = 0;
    bool measure = elapsed != NULL; // 判断是否需要进行统计时间

    status_t status;
    enum {
        TIMEOUT_ZERO,       // requested == NULL || *requested == 0 非阻塞
        TIMEOUT_INFINITE,   // *requested == infinity 无限等待
        TIMEOUT_FINITE,     // 0 < *requested < infinity 定时等待
        TIMEOUT_CONTINUE,   // additional chances after TIMEOUT_FINITE 定时等待的补充
    } timeout;
    // 默认是阻塞等待,是TIMEOUT_INFINITE
    if (requested == NULL) {
        timeout = TIMEOUT_ZERO;
    } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) {
        timeout = TIMEOUT_ZERO;
    } else if (requested->tv_sec == INT_MAX) {
        timeout = TIMEOUT_INFINITE;
    } else {
        timeout = TIMEOUT_FINITE;
        if (requested->tv_sec > 0 || requested->tv_nsec >= MEASURE_NS) {
            measure = true;
        }
    }
    struct timespec before;
    bool beforeIsValid = false;
    audio_track_cblk_t* cblk = mCblk;
    bool ignoreInitialPendingInterrupt = true;
    // 检查下share memory是否发生内存脏块,如果有问题,就直接go end
    if (mIsShutdown) {
        status = NO_INIT;
        goto end;
    }
    // 无限循环
    for (;;) {
        // 去掉CBLK_INTERRUPT 标志位。并且保存原来的值在flags中
        int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->mFlags);
        // 检查server是否将该track认为无效,或者server端挂掉了。
        if (flags & CBLK_INVALID) {
            status = DEAD_OBJECT;
            goto end;
        }
        // 检查下obtainBuffer过程是否被client给中断了。第一次不检测,后面都给检测。
        if (!ignoreInitialPendingInterrupt && (flags & CBLK_INTERRUPT)) {
            status = -EINTR;
            goto end;
        }
        ignoreInitialPendingInterrupt = false;
        // 计算多少帧的空间可以来写,或者多少帧的空间用来读。
        int32_t front;
        int32_t rear;
        if (mIsOut) {
            // 获取当前填充缓冲区的开头,这个变量是无限递增的
            front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
            // 获取当前填充缓冲区的结尾,这个变量是无限递增的。
            rear = cblk->u.mStreaming.mRear;
        } else {
            // On the other hand, this barrier is required.
            rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
            front = cblk->u.mStreaming.mFront;
        }
        // 计算出当前已经填充的量,就是尚未被消耗的帧。
        ssize_t filled = rear - front;
        // 如果filled的数值不在0到mFrameCount以内,说明内存已经被污染了。
        if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
            if (mIsOut) {
                // 设置为shutdown
                mIsShutdown = true;
                status = NO_INIT;
                goto end;
            }
            // 同步修改下flag添加overrun
            filled = 0;
            cblk->u.mStreaming.mFront = rear;
            (void) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags);
        }
        // 不允许填充的空间超过管道的大小,如果output的话,则是要填充的,所以是将mFrameCount - 
        // filled, 如果是record,是消费的,所以直接获取filled就行了。
        size_t avail = mIsOut ? mFrameCount - filled : filled;
        // 如果有可写的空间
        if (avail > 0) {
            // avail的空间不一定是连续的,可能是后面一节,前面一节,这个时候我们分两次取,先取后面
            size_t part1;
            if (mIsOut) {
                // mFrameCountP2这个是mFrameCount的roundup,这边用到了二进制的小技巧,
                // rear&roundup它的值很好计算。而且真实的空间mFrameCountP2就是这个,只要控制填充量
                // 不超过mFrameCount就行了。这些变量都是以帧为单位的
                rear &= mFrameCountP2 - 1;
                part1 = mFrameCountP2 - rear;
            } else {
                front &= mFrameCountP2 - 1;
                part1 = mFrameCountP2 - front;
            }
            // 如果part1 大于avail 那part1只能取到avail,大小我们还是要以mFrameCount的为准的。之
            // 所以申请mFrameCountP2的空间,完全是为了方便计算。
            if (part1 > avail) {
                part1 = avail;
            }
            // buffer->mFrameCount这个是用户写过来的数据,如果这个数据比part1还小,那我们只需要分配
            // 这个空间大小就够了。
            if (part1 > buffer->mFrameCount) {
                part1 = buffer->mFrameCount;
            }
            // 重新组装buffer,确定最后分配的空间。
            buffer->mFrameCount = part1;
            // 指向的内存空间
            buffer->mRaw = part1 > 0 ?
                    &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;
            // 还有多少可用的空间没被使用
            buffer->mNonContig = avail - part1;
            // 将申请到的空间大小赋值给mUnreleased,表示尚未被释放,等后面调用release时候,再更新
            mUnreleased = part1;
            status = NO_ERROR;
            // 如果申请到了空间,那我们就直接跳出这个for循环了。等待write函数中填充好数据在进来获取空
            // 间。
            break;
        }
        struct timespec remaining;
        const struct timespec *ts;
        switch (timeout) {
        case TIMEOUT_ZERO:
            status = WOULD_BLOCK;
            goto end;
        case TIMEOUT_INFINITE:
            ts = NULL;
            break;
        case TIMEOUT_FINITE:
            // 被修改成为TIMEOUT_CONTINUE,这边是为了防止平台设置MAX_SEC为0,可以采用自己申请的时间
            // MAC_SEC表示的是一次所等待的最大时间
            timeout = TIMEOUT_CONTINUE;
            if (MAX_SEC == 0) {
                ts = requested;
                break;
            }
            // fall through
        case TIMEOUT_CONTINUE:
            // FIXME we do not retry if requested < 10ms? needs documentation on this state machine
            if (!measure || requested->tv_sec < total.tv_sec ||
                    (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) {
                status = TIMED_OUT;
                goto end;
            }
            remaining.tv_sec = requested->tv_sec - total.tv_sec;
            if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) {
                remaining.tv_nsec += 1000000000;
                remaining.tv_sec++;
            }
            // 如果remaining的时间较长,则将强制为MAX_SEC,但是requested的时间并没有减少
            // 也就是说每次最多等待remaining的长度,知道requested都用光了。
            if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) {
                remaining.tv_sec = MAX_SEC;
                remaining.tv_nsec = 0;
            }
            ts = &remaining;
            break;
        default:
            LOG_ALWAYS_FATAL("obtainBuffer() timeout=%d", timeout);
            ts = NULL;
            break;
        }
        int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
        if (!(old & CBLK_FUTEX_WAKE)) {
            // 定时阻塞的,这边就会进入去记录阻塞之前的时间点。beforeIsValid一开始默认是false
            // 的。所以肯定进入了这个变量是保证只需要获取一次开始的,因为之后都是以上一次为准的。而且
            // 这个超时只针对单次超时,为什么不再进来,因为如果第二次到这说明上次也没获取到,时间
            // 上一致。
            if (measure && !beforeIsValid) {
                clock_gettime(CLOCK_MONOTONIC, &before);
                beforeIsValid = true;
            }
            errno = 0;
            // 注意,我们阻塞的时候,是有传入ts这个时间的,这个可以保证时间到了自动继续往下运行
            (void) syscall(__NR_futex, &cblk->mFutex,
                    mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
            // 阻塞结束之后要计算总共消耗时间。
            if (measure) {
                struct timespec after;
                clock_gettime(CLOCK_MONOTONIC, &after);
                total.tv_sec += after.tv_sec - before.tv_sec;
                long deltaNs = after.tv_nsec - before.tv_nsec;
                if (deltaNs < 0) {
                    deltaNs += 1000000000;
                    total.tv_sec--;
                }
                if ((total.tv_nsec += deltaNs) >= 1000000000) {
                    total.tv_nsec -= 1000000000;
                    total.tv_sec++;
                }
                // 更新下时间点,和beforeIsValid
                before = after;
                beforeIsValid = true;
            }
            // 正常唤醒、出错、超时等都会继续在跑循环。
            switch (errno) {
            case 0:            // normal wakeup by server, or by binderDied()
            case EWOULDBLOCK:  // benign race condition with server
            case EINTR:        // wait was interrupted by signal or other spurious wakeup
            case ETIMEDOUT:    // time-out expired
                break;
            default:
                status = errno;
                ALOGE("%s unexpected error %s", __func__, strerror(status));
                goto end;
            }
        }
    }

end:
    if (status != NO_ERROR) {
        buffer->mFrameCount = 0;
        buffer->mRaw = NULL;
        buffer->mNonContig = 0;
        mUnreleased = 0;
    }
    if (elapsed != NULL) {
        *elapsed = total;
    }
    if (requested == NULL) {
        requested = &kNonBlocking;
    }
    if (measure) {
    }
    return status;
}

这个函数看起来很长,但是其实它做的事情并不多。

  1. 获取缓冲区
  2. 如果获取不到进入阻塞,看情况需要做个计时的。

获取缓冲区,这边采用了小技巧。比如用户空间申请的framecount是666个这个是mFrameCount了,那TrackBase那边会给分配1024个这个就是P2了,rear和front都是无限递增的以帧为单位的int类型,它们是以P2为基准的,就是他们是P2的整数倍,这样我们再获取以填充的空间以及剩余可填充的空间就变得很简单了,直接rear-front就是可以填充的空间,剩余的空间就是mFrameCount-filled就可以得到了。

接着就是考虑如何实现循环缓冲了,其实就是引入part1,这个东西,它会让本来只需要填充一次变成需要填充两次,但是却可以简化编程,让逻辑更简单。如果写入的数据比较多,并且剩余的空间包括后面一小部分和前面一部分,那会先把后面这部分填充完,退出,然后让最外面的去release这个已经使用的部分,再来一次获取空间,这次获取的就是拿到前面这部分的缓冲区了。

接下来我们得来看看releaseBuffer的实现了。

void AudioTrack::releaseBuffer(const Buffer* audioBuffer)
{
    // mTransfer不能为TRANSFER_SHARED
    if (mTransfer == TRANSFER_SHARED) {
        return;
    }
    // 计算这次填充的空间,转换成帧
    size_t stepCount = audioBuffer->size / mFrameSize;
    if (stepCount == 0) {
        return;
    }

    Proxy::Buffer buffer;
    buffer.mFrameCount = stepCount;
    buffer.mRaw = audioBuffer->raw;

    AutoMutex lock(mLock);
    // release做累加
    mReleased += stepCount;
    mInUnderrun = false;
    // 调用proxy的releaseBuffer
    mProxy->releaseBuffer(&buffer);

    // 重启track 如果之前被AudioFlinger因为underrun给disable了
    if (mState == STATE_ACTIVE) {
        audio_track_cblk_t* cblk = mCblk;
        if (android_atomic_and(~CBLK_DISABLED, &cblk->mFlags) & CBLK_DISABLED) {
            mAudioTrack->start();
        }
    }
}

上面的函数,做个统计,然后就调用proxy的releaseBuffer了。

void ClientProxy::releaseBuffer(Buffer* buffer)
{
    LOG_ALWAYS_FATAL_IF(buffer == NULL);
    size_t stepCount = buffer->mFrameCount;
    if (stepCount == 0 || mIsShutdown) {
        // prevent accidental re-use of buffer
        buffer->mFrameCount = 0;
        buffer->mRaw = NULL;
        buffer->mNonContig = 0;
        return;
    }
    // 更新下mUnreleased的值。表示还有多少分配给client的空间未被填充。
    mUnreleased -= stepCount;
    audio_track_cblk_t* cblk = mCblk;
    // 更新下缓冲区的index。这边可以看出rear和front是一个一直递增的值。
    if (mIsOut) {
        int32_t rear = cblk->u.mStreaming.mRear;
        android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
    } else {
        int32_t front = cblk->u.mStreaming.mFront;
        android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
    }
}

消费者部分

前面我们有提到,消费者是一个线程,这个线程,在我们当前这个场景下是一个普通的mixer thread,其实也是我们前面一直有出现的output thread,这个线程主要就是负责获取数据,mix数据,效果处理等一系列的操作的。每一个普通的mixer thread,它都可能会包含好多个的track,这样的话,它的实现上必然就不能为等待某个track的数据而发生阻塞,只有当所有的track都没数据了,它才会进入休眠状态。
当然这边我们讲解的主要场景是获取数据了,获取数据其实是在AudioMixer里面来执行的,但是我们不能只关注获取,我们需要关注的是流程如何控制到获取数据的整个逻辑。所以我们从MixerThread::prepareTracks_l,这边我会先把fast track相关的部分先省略掉,因为我们目前只定位在normal track,其实fast track的逻辑也是差不多的,只是细节实现上更复杂一些。

AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
        Vector< sp<Track> > *tracksToRemove)
{

    mixer_state mixerStatus = MIXER_IDLE;
    // 看有多少个track需要被处理的
    size_t count = mActiveTracks.size();
    size_t mixedTracks = 0;
    size_t tracksWithEffect = 0;

    float masterVolume = mMasterVolume;
    bool masterMute = mMasterMute;

    if (masterMute) {
        masterVolume = 0;
    }
    // 如果存在全局音效的话,委托全局音效来控制master volume。
    sp<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();
    }

    mMixerBufferValid = false;  // 有使用mixerbuffer的tracks被找到并且有数据会被置为true
    mEffectBufferValid = false; // 有使用mixerbuffer的tracks被找到并且有数据会被置为true

    for (size_t i=0 ; i<count ; i++) {
        const sp<Track> t = mActiveTracks[i].promote();
        if (t == 0) {
            continue;
        }

        Track* const track = t.get();

        {   // local variable scope to avoid goto warning

        audio_track_cblk_t* cblk = track->cblk();

        // 对于第一次被添加进来的track,我们会等待它把buffer都填充满才来处理的。
        int name = track->name();
        // 为了防止用户在结束的时候没有通过调用stop函数来终止播放,而是依赖underrun来结束,添加了变量
        // mMixerStatus来记录上一次的track的mix的情况。
        // mMixerStatus == MIXER_TRACKS_READY意味着上一次的循环,track已经被mixed过了。
        // 也侧面反映了,上一次还是有数据的,如果上一次没有被mixed过的话,那我们就直接采用最小的来帮忙
        // 将剩下的数据排空
        size_t desiredFrames;
        const uint32_t sampleRate = track->mAudioTrackServerProxy->getSampleRate();
        AudioPlaybackRate playbackRate = track->mAudioTrackServerProxy->getPlaybackRate();
        // 确保有足够的数据来填充满hal层的一个buffer
        // 计算hal层buffer映射到当前速率,采样率下是多少帧
        desiredFrames = sourceFramesNeededWithTimestretch(
                sampleRate, mNormalFrameCount, mSampleRate, playbackRate.mSpeed);
        // 数据已经被AudioMixer获取,但是尚未release,所以需要加上这个部分,因为此时client还没呈现
        // 出来变化
        desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());

        uint32_t minFrames = 1;
        // minFrames需要AudioMixer在上一轮的操作中已经被enable了,并且track的需要满足下面条件
        // 才会被设置为desiredFrames,为什么pausing的时候,也不给机会呢,这个时候缓冲区的数据可能
        // 没办法完全满足desiredFrames,但是我们依然要制造volume ramp,所以只能这么做了。
        if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
                (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
            minFrames = desiredFrames;
        }

        size_t framesReady = track->framesReady();
        // 对于pause的场景,是会直接戛然而止,而不会将app写进去尚未消费的数据用掉
        // 对于stop的场景, 是会将数据都用光的。
        if ((framesReady >= minFrames) && track->isReady() &&
                !track->isPaused() && !track->isTerminated())
        {
            mixedTracks++;

            chain.clear();
            if (track->mainBuffer() != mSinkBuffer &&
                    track->mainBuffer() != mMixerBuffer) {
                if (mEffectBufferEnabled) {
                    mEffectBufferValid = true; // Later can set directly.
                }
                // 能进入到这边,说明这个track关联到了一个效果链中。
                chain = getEffectChain_l(track->sessionId());
                if (chain != 0) {
                    tracksWithEffect++;
                } else {
                }
            }

            int param = AudioMixer::VOLUME;
            if (track->mFillingUpStatus == Track::FS_FILLED) {
                // no ramp for the first volume setting
                // 对于第一次的音量设置,不采用ramp volume,一旦进来就被设置为FS_ACTIVE了。
                track->mFillingUpStatus = Track::FS_ACTIVE;
                // RESUMING只有pausing或者PAUSED才可能触发。
                // 可以想到的场景就是,start了之后数据尚未满足ready,然后就被pause,然后再被start。
                if (track->mState == TrackBase::RESUMING) {
                    track->mState = TrackBase::ACTIVE;
                    param = AudioMixer::RAP_MVOLUME;
                }
                mAudioMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
            } else if (cblk->mServer != 0) {
                param = AudioMixer::RAMP_VOLUME;
            }

            // compute volume for this track
            uint32_t vl, vr;       // in U8.24 integer format
            float vlf, vrf, vaf;   // in [0.0, 1.0] float format
            // 对于pause的场景或者静音,添加一个volume ramp的效果,也增加出pausing的状态。
            if (track->isPausing() || mStreamTypes[track->streamType()].mute) {
                vl = vr = 0;
                vlf = vrf = vaf = 0.;
                if (track->isPausing()) {
                    track->setPaused();
                }
            } else {

                // read original volumes with volume control
                float 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 clamped
                if (vlf > GAIN_FLOAT_UNITY) {
                    vlf = GAIN_FLOAT_UNITY;
                }
                if (vrf > GAIN_FLOAT_UNITY) {
                    vrf = GAIN_FLOAT_UNITY;
                }
                // now apply the master volume and stream type volume
                vlf *= 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 chain
                const 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 format
                uint16_t sendLevel = proxy->getSendLevel_U4_12();
                // send level comes from shared memory and so may be corrupt
                if (sendLevel > MAX_GAIN_INT) {
                    sendLevel = MAX_GAIN_INT;
                }
                // vaf is represented as [0.0, 1.0] float by rescaling sendLevel
                vaf = v * sendLevel * (1. / MAX_GAIN_INT);
            }

            // 将音量控制委托给效果链
            if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
                // 如果效果链掌控音量控制,则不要使用ramp volume
                param = AudioMixer::VOLUME;
                // 更新下音量值
                vlf = (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 spike
                if (track->mHasVolumeController) {
                    param = AudioMixer::VOLUME;
                }
                track->mHasVolumeController = false;
            }

            // XXX: these things DON'T need to be done each time
            mAudioMixer->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-configuration
            uint32_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);

            // 如果mMixerBufferEnabled并且当前这个track的输出buffer是sinkbuffer或者
            // mixerbuffer,则设置输出buffer为mixerbuffer,否则的话都设置为track之前设置的
            // buffer。
            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);
                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 count
            track->mRetryCount = kMaxTrackRetries;

            // 如果上一次没有ready,这次只要有一个track已经ready,则为ready
            if (mMixerStatusIgnoringFastTracks != MIXER_TRACKS_READY ||
                    mixerStatus != MIXER_TRACKS_ENABLED) {
                mixerStatus = MIXER_TRACKS_READY;
            }
        } else {
            if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
                track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
            }
            // clear effect chain input buffer if an active track underruns to avoid sending
            // previous audio buffer again to effects
            chain = getEffectChain_l(track->sessionId());
            if (chain != 0) {
                chain->clearInputBuffer();
            }
            // 如果track是使用共享内存,或者已经终止、停止、暂停,则会进入下面逻辑。 
            if ((track->sharedBuffer() != 0) || track->isTerminated() ||
                    track->isStopped() || track->isPaused()) {
                // 会在继续填充latency的时间长度的空白数据,这个有优化空间
                size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
                size_t framesWritten = mBytesWritten / mFrameSize;
                if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
                    // 如果track是stopped状态,则重置track。
                    if (track->isStopped()) {
                        track->reset();
                    }
                    // 加入到移除队列
                    tracksToRemove->add(track);
                }
            } else {
                // 如果不是共享内存,并且当前状态不是终止、停止、暂停,就说明肯定就是数据给的不够。
                // 这个时候会给它50次的机会进行填充,如果还不满足就移掉active list了。
                if (--(track->mRetryCount) <= 0) {
                    tracksToRemove->add(track);
                    // 告诉app的track由于underrun被disable,如果数据够,会自动调用start()方法
                    android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
                // 只要有一个active track尚未ready,那就标志mixer states为not ready
                // 但是这边有个例外的,上一次已经设置为enable,并且有两个track,一个ready,另外
                // 一个not ready,则这次的mixer states会被设置为READY
                } else if (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY ||
                                mixerStatus != MIXER_TRACKS_READY) {
                    mixerStatus = MIXER_TRACKS_ENABLED;
                }
            }
            // 这边会将AudioMixer disable。
            mAudioMixer->disable(name);
        }

        }   // local variable scope to avoid goto warning
track_is_ready: ;

    }

    // Push the new FastMixer state if necessary
    bool pauseAudioWatchdog = false;
#ifdef AUDIO_WATCHDOG
    if (pauseAudioWatchdog && mAudioWatchdog != 0) {
        mAudioWatchdog->pause();
    }
#endif

    // remove all the tracks that need to be...
    removeTracks_l(*tracksToRemove);
    // 如果有全局效果链的话,则设置状态位。
    if (getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX) != 0) {
        mEffectBufferValid = true;
    }

    if (mEffectBufferValid) {
        // 有效果的话,我们需要把effect buffer清空,避免有不干净的数据进入到效果链中
        memset(mEffectBuffer, 0, mEffectBufferSize);
    }
    // 如果所有的track都链接到一个效果链,这种情况下audiomixer将不会写数据到sink或者mixbuffer,
    // 并且track的效果将会添加进来,所以我们需要清空sink和mix的buffer。
    if ((mBytesRemaining == 0) && ((mixedTracks != 0) && (mixedTracks == tracksWithEffect) || (mixedTracks == 0 && fastTracks > 0))) {
        if (mMixerBufferValid) {
            memset(mMixerBuffer, 0, mMixerBufferSize);
            // mSinkBuffer最后会从MixerBuffer中拷贝数据到SinkBuffer,所以不需要清空
        }
        memset(mSinkBuffer, 0, mNormalFrameCount * mFrameSize);
    }

    // mMixerStatusIgnoringFastTracks这个是用来对付normal track的,而之前mMixerStatus用来对付全
    // 局的
    mMixerStatusIgnoringFastTracks = mixerStatus;
    if (fastTracks > 0) {
        mixerStatus = MIXER_TRACKS_READY;
    }
    return mixerStatus;
}

这个函数其实蛮复杂的,因为它不仅关注了这个thread中的所有track的状态,还需要对缓冲区进行清理等工作。

我们来一起总结下,先看下只有一个track的情况:

  1. 如果该track被添加到active list,那这边就可以检索到了,fast的先略过,那这个函数会去当前的track的状态,以及是否上次循环已经有mixed过了,来决定它的填充帧数的要求;
  2. 判断是否ready了,第一次的填充需要将app申请缓冲区都填满,之后就会将mFillingUpStatus设置为filled,那之后这边就直接pass了
  3. 判断暂停、停止、终止等状态
  4. 如果以上都pass的话,那就会开始进行audiomixer的设置,包括音量,缓冲区,格式等一系列可能需要在mixer中完成的参数告诉它,因为可能牵扯到downmix、reformat、resampler等等。并且使能audiomixer
  5. 更新下mMixerStatus和mMixerStatusIgnoringFastTracks
  6. 如果有效果的话,则对缓冲区进行相关清理等工作。

一个track,但是只有play,没有write操作:

  1. 判断是否ready的时候肯定就fail了,所以进入到else的分支中。
  2. 此时的帧数肯定不满足要求的,并且状态不是停止或暂停,所以会告诉client你已经underrun了
  3. 获取效果链,清除输入buffer,开始计数underrun的次数,如果达到50次,将移除该track
  4. 没有达到的话,则更新下mMixerStatus和mMixerStatusIgnoringFastTracks
  5. disable audiomixer
  6. 将track移除操作

一个track,paly、write、之后stop,直接从stop开始分析:

  1. 接收到stop之后,minFrames就只会等于1了
  2. 然后会进入enable audiomixer的流程中,将client端写入的数据全部用完
  3. 用完之后,在进入的时候就没办法pass enable的流程,这个时候就会进入到else分支中
  4. 发现是stop的情况,会进行latency时间长度的填充空白数据的流程
  5. 等填充满latency时间长度之后,就将该track remove掉,会reset该track

一个track,play、write、之后pause,直接从pause开始:

  1. 接收到pause之后,会先进入pausing的过程,这个时候minFrames也是为1的
  2. 然后会进入到enable audiomixer的流程中,这个时候会将pausing转变为paused的状态,并且音量设置为0,实现volume ramp的效果
  3. 下一次就无法进入enable audiomixer的流程,进入到else分支中
  4. 发现是paused的情况,会进行latency时间长度的填充空白数据的流程
  5. 等填充慢lantecy时间长度之后,就将该track remove掉,不会有reset track的流程

2个track,一个play,write,一个只play,不write:
这种情况只是想要分析下mMixerStatus和mMixerStatusIgnoringFastTracks的状态,因为这个状态的更新都有对上一次的循环的mMixerStatusIgnoringFastTracks做判断的。所以它是具有独特性的,这种状态下面mMixerStatusIgnoringFastTracks的状态变化是,ready和enable交替出现的。

enable的话是不会触发audiomixer的执行流程的,只有ready才会触发该audiomixer的执行。如下面代码

if (mMixerStatus == MIXER_TRACKS_READY) {
    // threadLoop_mix() sets mCurrentWriteLength
    threadLoop_mix();
} else if ((mMixerStatus != MIXER_DRAIN_TRACK)
            && (mMixerStatus != MIXER_DRAIN_ALL)) {
    // mSleepTimeUs如果为0表示有数据要马上写到底层,就是触发threadloop_write的逻辑,否则sleep
    threadLoop_sleepTime();
    if (mSleepTimeUs == 0) {
        mCurrentWriteLength = mSinkBufferSize;
    }
}

一次ready一次enable的话,那就是mix一次之后要sleep一次,再mix一次,再sleep一次,第一次是mix的,所以sleeptime在mix的函数中被设置为0了。第二次是enable。

void AudioFlinger::MixerThread::threadLoop_sleepTime()
{
    if (mSleepTimeUs == 0) {
        if (mMixerStatus == MIXER_TRACKS_ENABLED) {
            // 将时间进行缩减, mActiveSleepTimeUs时间相当于hal buffer排空所需要的时间
            mSleepTimeUs = mActiveSleepTimeUs >> sleepTimeShift;
            // 有个最小的阈值
            if (mSleepTimeUs < kMinThreadSleepTimeUs) {
                mSleepTimeUs = kMinThreadSleepTimeUs;
            }
            // 我们再不停的缩减sleeptime的时间,就是为了防止app underruns个没玩,导致hal层无法播放
            // 断续问题。不能因为其中一个underrun导致,另外一个track的数据供应也出问题,所以时间上
            // 是非常讲究的
            if (sleepTimeShift < kMaxThreadSleepTimeShift) {
                sleepTimeShift++;
            }
        } else {
            mSleepTimeUs = mIdleSleepTimeUs;
        }
    } else if (mBytesWritten != 0 || (mMixerStatus == MIXER_TRACKS_ENABLED)) {
        // 需要已经连续两次的mix status不为ready才会进来到这了。基本上表示没有数据了。
        // 在效果执行之前,我们根据情况清空对应的buffer
        if (mMixerBufferValid) {
            memset(mMixerBuffer, 0, mMixerBufferSize);
        } else {
            memset(mSinkBuffer, 0, mSinkBufferSize);
        }
        // 然后将时间又设置为0
        mSleepTimeUs = 0;
    }
}

上面函数的逻辑就是怎么计算sleeptime了。一旦出现mixer status状态不为ready我们就需要考虑睡多久的问题,这样将本来需要process的时间跟pass掉,可以起到降低功耗的作用。但是对于那个有write的track来说,本来是一直getbuffer->mix->write,现在则是getbuffer->mix->write->sleep->getbuffer->mix->write,而sleep的时间长度虽然是递减的,基本上变成了往底层写一个buffer的大小的数据,然后再等待数据播放完成之后,再写一个buffer的数据。

sleeptime相关write逻辑:

// 如果mSleepTimeUs为0并且有mix、effect了一些数据,那就执行写操作,更新下写的数量和剩余的数量
if (mSleepTimeUs == 0) {
    ssize_t ret = 0;
    if (mBytesRemaining) {
        ret = threadLoop_write();
        if (ret < 0) {
            mBytesRemaining = 0;
        } else {
            mBytesWritten += ret;
            mBytesRemaining -= ret;
        }
    // 如果当前mixer的状态为MIXER_DRAIN_TRACK和MIXER_DRAIN_ALL,则进行drain的操作
    } else if ((mMixerStatus == MIXER_DRAIN_TRACK) ||
            (mMixerStatus == MIXER_DRAIN_ALL)) {
        threadLoop_drain();
    }
    // 这是针对mixer线程的
    if (mType == MIXER && !mStandby) {
        // write blocked detection
        nsecs_t now = systemTime();
        // 写数据阻塞用掉的时间
        nsecs_t delta = now - mLastWriteTime;
        // 阻塞时间太长的话,进行一些统计等。
        if (delta > maxPeriod) {
            mNumDelayedWrites++;
            if ((now - lastWarning) > kWarningThrottleNs) {
                lastWarning = now;
            }
        }
        // mThreadThrottle默认开启,并且写入一些数据了
        if (mThreadThrottle
                && mMixerStatus == MIXER_TRACKS_READY // we are mixing (active tracks)
                && ret > 0) {                         // we wrote something
            // 这边是用来限制这个loop跑得太快的。要求是小于2倍的预期的,也就是halfbuffer的长度
            // 同时也有助于app那边填充数据。如果这个线程跑太快,app线程慢了的话,就会出现underrun了
            // 当然是针对小buffer的场景

            const int32_t deltaMs = delta / 1000000;
            // throttleMs这个大于0的话,说明底层数据所剩得不是很多,上层数据给的比较慢,所以我们要
            // 等等上层。如果小于0的话,则说明底层数据有点应付不过来了,那我们就不需要等待了。
            const int32_t throttleMs = mHalfBufferMs - deltaMs;
            // 如果halfbuffer的时间大于等于throttleMs,并且throttleMs大于0,则进行sleep来耗时间
            if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) {
                usleep(throttleMs * 1000);
                mThreadThrottleTimeMs += throttleMs;
            } else {
                // 这边的代码则是完全为了方便debug。
                uint32_t diff = mThreadThrottleTimeMs - mThreadThrottleEndMs;
                if (diff > 0) {
                    mThreadThrottleEndMs = mThreadThrottleTimeMs;
                }
            }
        }
    }

} else {
    ATRACE_BEGIN("sleep");
    usleep(mSleepTimeUs);
    ATRACE_END();
}

我们之前只是看到prepareTracks_l那边有去判断是否数据准备好了,有去enable audiomixer,那获取数据,消费数据的又是在哪里做的呢?

其实就在我们audiomixer里面,是通过threadloop_mix进去的。我们直接拿audiomixer里面的代码来看就好了:

void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts)
{
    // 返回左起第一个‘1’之前0的个数。i表示的是右边算起来第几位
    const int i = 31 - __builtin_clz(state->enabledTracks);
    track_t *t = &state->tracks[i];
    const uint32_t channels = t->mMixerChannelCount;
    // 获取输出的buffer
    TO* out = reinterpret_cast<TO*>(t->mainBuffer);
    TA* aux = reinterpret_cast<TA*>(t->auxBuffer);
    const bool ramp = t->needsRamp();
    // state->frameCount这个值是创建audiomixer的时候给的。为NormalFrameCount,非fast的话,就是
    // hal层的buffer大小了
    for (size_t numFrames = state->frameCount; numFrames; ) {
        // 获取该track的buffer对象
        AudioBufferProvider::Buffer& b(t->buffer);
        // 组装buffer信息
        b.frameCount = numFrames;
        const int64_t outputPTS = calculateOutputPTS(*t, pts, state->frameCount - numFrames);
        // 触发track getNextBuffer被调用,该函数也只是简单的封装对象给serverproxy的obtainBuffer
        // 然后将输出结果进行封装,返回回来。
        t->bufferProvider->getNextBuffer(&b, outputPTS);
        // 获取到的数据的指针位置
        const TI *in = reinterpret_cast<TI*>(b.raw);
.
        // track被flush或者没有数据,为什么&3,因为采样精度是8的倍数
        if (in == NULL || (((uintptr_t)in) & 3)) {
            memset(out, 0, numFrames
                    * channels * audio_bytes_per_sample(t->mMixerFormat));
            return;
        }
        // 获取到的具体的数据量
        const size_t outFrames = b.frameCount;
        // 给弄上声音,拷贝到out中
        volumeMix<MIXTYPE, is_same<TI, float>::value, false> (
                out, outFrames, in, aux, ramp, t);
        // out索引更新
        out += outFrames * channels;
        if (aux != NULL) {
            aux += channels;
        }
        numFrames -= b.frameCount;

        // release buffer
        t->bufferProvider->releaseBuffer(&b);
    }
    if (ramp) {
        t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value);
    }
}

该函数是audiomixer对于一个track并且不需要resampler的处理,其他的resampler或者多个track的会复杂一些,但是获取数据、消费数据是一致的。resampler和多个track等audiomixer的场景,我们后面再来分析重要的函数,看是如何处理buffer这个问题的。

接下来我们直接来看serverproxy中的obtainBufferreleaseBuffer

status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
{
    LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0);
    if (mIsShutdown) {
        goto no_init;
    }
    {
    audio_track_cblk_t* cblk = mCblk;
    int32_t front;
    int32_t rear;
    if (mIsOut) {
        // 相对于client的实现,多出了flush的部分。其他的算法思路都跟client是一样的。
        int32_t flush = cblk->u.mStreaming.mFlush;
        rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
        front = cblk->u.mStreaming.mFront;
        if (flush != mFlush) {
            // effectively obtain then release whatever is in the buffer
            const size_t overflowBit = mFrameCountP2 << 1;
            const size_t mask = overflowBit - 1;
            int32_t newFront = (front & ~mask) | (flush & mask);
            ssize_t filled = rear - newFront;
            if (filled >= (ssize_t)overflowBit) {
                // front and rear offsets span the overflow bit of the p2 mask
                // so rebasing newFront on the front offset is off by the overflow bit.
                // adjust newFront to match rear offset.
                ALOGV("flush wrap: filled %zx >= overflowBit %zx", filled, overflowBit);
                newFront += overflowBit;
                filled -= overflowBit;
            }
            // Rather than shutting down on a corrupt flush, just treat it as a full flush
            if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
                newFront = rear;
            }
            mFlush = flush;
            android_atomic_release_store(newFront, &cblk->u.mStreaming.mFront);
            // There is no danger from a false positive, so err on the side of caution
            if (true /*front != newFront*/) {
                int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
                if (!(old & CBLK_FUTEX_WAKE)) {
                    (void) syscall(__NR_futex, &cblk->mFutex,
                            mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
                }
            }
            front = newFront;
        }
    } else {
        front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
        rear = cblk->u.mStreaming.mRear;
    }
    ssize_t filled = rear - front;
    // pipe should not already be overfull
    if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
        mIsShutdown = true;
    }
    if (mIsShutdown) {
        goto no_init;
    }
    // don't allow filling pipe beyond the nominal size
    size_t availToServer;
    if (mIsOut) {
        availToServer = filled;
        mAvailToClient = mFrameCount - filled;
    } else {
        availToServer = mFrameCount - filled;
        mAvailToClient = filled;
    }
    // 'availToServer' may be non-contiguous, so return only the first contiguous chunk
    size_t part1;
    if (mIsOut) {
        front &= mFrameCountP2 - 1;
        part1 = mFrameCountP2 - front;
    } else {
        rear &= mFrameCountP2 - 1;
        part1 = mFrameCountP2 - rear;
    }
    if (part1 > availToServer) {
        part1 = availToServer;
    }
    size_t ask = buffer->mFrameCount;
    if (part1 > ask) {
        part1 = ask;
    }
    // is assignment redundant in some cases?
    buffer->mFrameCount = part1;
    buffer->mRaw = part1 > 0 ?
            &((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL;
    buffer->mNonContig = availToServer - part1;
    // After flush(), allow releaseBuffer() on a previously obtained buffer;
    // see "Acknowledge any pending flush()" in audioflinger/Tracks.cpp.
    if (!ackFlush) {
        mUnreleased = part1;
    }
    return part1 > 0 ? NO_ERROR : WOULD_BLOCK;
    }
no_init:
    buffer->mFrameCount = 0;
    buffer->mRaw = NULL;
    buffer->mNonContig = 0;
    mUnreleased = 0;
    return NO_INIT;
}

flush的部分现在不想分析,其他的逻辑又跟clientproxy中obtainBuffer的实现没啥区别,就是少了一个等待时间的统计。

接着看releaseBuffer吧。

void ServerProxy::releaseBuffer(Buffer* buffer)
{
    LOG_ALWAYS_FATAL_IF(buffer == NULL);
    size_t stepCount = buffer->mFrameCount;
    if (stepCount == 0 || mIsShutdown) {
        // prevent accidental re-use of buffer
        buffer->mFrameCount = 0;
        buffer->mRaw = NULL;
        buffer->mNonContig = 0;
        return;
    }
    // 更新下有多少个尚未被消费
    mUnreleased -= stepCount;
    audio_track_cblk_t* cblk = mCblk;
    if (mIsOut) {
        // 更新front的位置,这边可以看出front也是无限递增的
        int32_t front = cblk->u.mStreaming.mFront;
        android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
    } else {
        int32_t rear = cblk->u.mStreaming.mRear;
        android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
    }
    // server的指针也跟着挺近。
    cblk->mServer += stepCount;

    size_t half = mFrameCount / 2;
    if (half == 0) {
        half = 1;
    }
    // 这个是我们一开始createTrack的时候就已经给到的值,意思就是消费了多少就可以唤醒client的意思
    size_t minimum = (size_t) cblk->mMinimum;
    if (minimum == 0) {
        // 为0的话,我们就使用申请buffer的一半,否则就为1
        minimum = mIsOut ? half : 1;
    } else if (minimum > half) {
        // 如果大于一半,那我们强制用一半,就是可以早点通知client去生产数据
        minimum = half;
    }
    if (!mIsOut || (mAvailToClient + stepCount >= minimum)) {
        // 开始唤醒操作
        int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
        if (!(old & CBLK_FUTEX_WAKE)) {
            (void) syscall(__NR_futex, &cblk->mFutex,
                    mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
        }
    }

    buffer->mFrameCount = 0;
    buffer->mRaw = NULL;
    buffer->mNonContig = 0;
}

该函数主要实现:

  1. 更新mUnreleased、front、server等信息
  2. 确认什么时候唤醒client生产数据,如果达到,就去唤醒。

到此,我们算完成了生产者消费者模型的分析,一开头就已经总结了整个机制。