Android音视频四:AudioTrack数据传递

AudioTrack的共享内存

AudioTrack的两种模式

APP给AudioTrack提供音频数据有2种模式:

MODE_STATIC模式:一次性提前提供数据。使用APP创建共享内存, APP一次性填充数据。playbackthread等数据构造好,取出数据就可以直接使用了,不存在同步问题。对应的playbackThread工作是:获得含有数据的obtainBuffer(APP一次性提交共享内存的数据有可能很多,playbackThread需要进行多次播放)。完成后释放buffer。

MODE_STREAM模式:边播放边提供。使用playbackThread创建共享内存。APP使用obtainBuffer获得buffer, 填充数据后使用releaseBuffer释放buffer。playbackThread一样需要进行多次播放。只不过这里使用的是环形缓冲区机制来,不断传递数据。完成后释放buffer。

从AudioTrack构造看内存共享

# Java::AudioTrack->Java::native_setup->JNI转换->android_media_AudioTrack_setup

static jint android_media_AudioTrack_setup(....) {
        // lyh 两种模式
        switch (memoryMode) {
        
        // lyh 写一段播一段
        case MODE_STREAM:
            status = lpTrack->set(AUDIO_STREAM_DEFAULT,...
                                        // shared mem = 0 内存后面由playbackthread来申请
                                        0,....);
            break;
            
        // lyh 一次写完播放
        case MODE_STATIC:
            // lyh 应用端申请共享内存
            if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
                goto native_init_failure;
            }

            status = lpTrack->set(AUDIO_STREAM_DEFAULT,...
                                    // shared mem = 应用端创建的共享内存
                                    lpJniStorage->mMemBase,....);
            break;
        }
}


复制代码
# AudioTrack::set-> AudioTrack::createTrack_l-> Track的创建

//lyh 构建aduiotrack的流程
status_t AudioTrack::createTrack_l() {

    sp<IAudioTrack> track = audioFlinger->createTrack(input,
                                                      output,
                                                      &status);

   /* lyh
     *获取track变量 的共享内存 buffer
     *当PlaybackThread创建一个PlaybackThread::Track对象时,所需的缓冲区空间
     *就已经分配了,这块空间是可以跨进程共享的,所以AudioTrack可以通过track->getCblk
     *
    **/
    sp<IMemory> iMem = track->getCblk();
    void *iMemPointer = iMem->unsecurePointer();
    audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
    
    // mSharedBuffer = App端创建的共享内存 = MODE_STATIC
    void* buffers;
    if (mSharedBuffer == 0) {
        buffers = cblk + 1;
    } else {
        buffers = mSharedBuffer->unsecurePointer();
    }
    
    // 创建对应的client代理
    if (mSharedBuffer == 0) {
        // MODE_STREAM模式
        // 指向 playbackthread提供的Buffer
        mProxy = new AudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);
    } else {
        // MODE_STATIC模式(看名字也知道)
        // 指向应用端APP提供的Buffer
        mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);
        mProxy = mStaticProxy;
    }

}

复制代码

PlaybackThread的Track建立共享内存

AudioFlinger::ThreadBase::TrackBase::TrackBase(  
            void *buffer, // 如果应用端创建了内存共享块,就是这个值
            size_t bufferSize, 
) {
    
    
    size_t minBufferSize = buffer == NULL ? roundup(frameCount) : frameCount;

    if (buffer == nullptr) {
        bufferSize = minBufferSize; 
    }
    
    // lyh 计算结构体的
    size_t size = sizeof(audio_track_cblk_t);
    if (buffer == NULL && alloc == ALLOC_CBLK) {
        size += bufferSize;
    }
    
    // lyh 从之前文章流程来看,client是一定有值的
    if (client != 0) {
        // lyh 分配一块匿名共享内存
        mCblkMemory = client->heap()->allocate(size);
    } else {
        mCblk = (audio_track_cblk_t *) malloc(size);
    }
    
        if (mCblk != NULL) {
        // lyh 定位创建对象,可以在特定位置创建对象
        // lyh 这里就是在匿名共享内存的首地址创建一个audio_track_cblk_t对象
        new(mCblk) audio_track_cblk_t();
        switch (alloc) {
        case ....
        case ALLOC_CBLK:
            // clear all buffers
            // lyh buffer的初始化
            if (buffer == NULL) {
                // lyh 指向playbackthread提供的Buffer
                mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
                memset(mBuffer, 0, bufferSize);
            } else {
                // lyh 指向APP提供的Buffer
                mBuffer = buffer;
            }
        case ....
        }
        mBufferSize = bufferSize;
    
}

复制代码
AudioFlinger::PlaybackThread::Track::Track(...){

    // sharedBuffer == 0 -> MODE_STREAM
    if (sharedBuffer == 0) {
        mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
                mFrameSize, !isExternalTrack(), sampleRate);
    } else {
        mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,mFrameSize, sampleRate);
    }
    mServerProxy = mAudioTrackServerProxy;

}

复制代码

总的来说 上面的过程就是这两句话

  1. AudioTrack中使用AudioTrackClientProxy对象 和 StaticAudioTrackClientProxy对象来管理共享内存。
  2. Track中使用AudioTrackServerProxy对象 和 StaticAudioTrackServerProxy对象 来管理共享内存。
  3. 环形缓冲区感兴趣可以了解一下,个人感觉就是一个双指针的算法题

工作流程

  1. AudioTrack(APP应用端)通过mClientProxy向共享buffer写入数据, AudioFlinger(server端)通过 mServerProxy从共享内存中读出数据。 这样client、server通过proxy对共享内存形成了生产者——消费者模式

  2. Client端: AudioTrackClientProxy:: obtainBuffer()从 audio buffer 获取连续的空buffer; AudioTrackClientProxy:: releaseBuffer ()将填充了数据的 buffer 放回 audio buffer。

  3. Server端: AudioTrackServerProxy:: obtainBuffer()从 audio buffer 获取连续的填充了数据的 buffer; AudioTrackServerProxy:: releaseBuffer()将使用完的空buffer 放回 audio buffer。

AudioTrack数据的write和play流程

wrtie

obtainBuffer最终是调用AudioTrackClientProxy的方法,主要就是从共享内存中取出空的buffer,将音频数据写入

ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{

    size_t written = 0;
    Buffer audioBuffer;

    // lyh userSize 用户可用空间
    while (userSize >= mFrameSize) {
        audioBuffer.frameCount = userSize / mFrameSize;
        // lyh 获取buffer中的空余部分
        status_t err = obtainBuffer(&audioBuffer,
                blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
        if (err < 0) {
            if (written > 0) {
                break;
            }
            if (err == TIMED_OUT || err == -EINTR) {
                err = WOULD_BLOCK;
            }
            return ssize_t(err);
        }

        size_t toWrite = audioBuffer.size;
        // lyh 做数据拷贝 保存在i8变量中
        memcpy(audioBuffer.i8, buffer, toWrite);


        buffer = ((const char *) buffer) + toWrite;
        // lyh 剩余可用和已经写入
        userSize -= toWrite;
        written += toWrite;

        // lyh 释放
        releaseBuffer(&audioBuffer);
    }



    return written;
}

复制代码

Play

  1. 调用流程:从Java层调用Play -> JNI的native_start -> NAtive层面AudioTrack的start -> Binder通信 -> Track的start
  2. 总结:拿到到playbackThread回放线程,然后添加Track,开启Server端去管理共享内存块,接着通过广播环形Playback::threadloop方法,
// lyh AudioTrack.cpp#mAudioTrack->start方法最后流入这
status_t AudioFlinger::PlaybackThread::Track::start(...)
{

    // lyh 
    sp<ThreadBase> thread = mThread.promote();
    if (thread != 0) {

        if (state == PAUSED || state == PAUSING) {
            ...
        } else {
            // lyh 状态修改
            mState = TrackBase::ACTIVE;
        }

        PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
        // lyh 
        status = playbackThread->addTrack_l(this);


        if (status == NO_ERROR || status == ALREADY_EXISTS) {
            // for streaming tracks, remove the buffer read stop limit.
            // lyh Server端的Proxy开始管理共享内存
            mAudioTrackServerProxy->start();
        }
    }
    return status;
}

复制代码
status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{
    status_t status = ALREADY_EXISTS;

    // lyh 是新增的track
    if (mActiveTracks.indexOf(track) < 0) {
        if (track->isExternalTrack()) {
            // lyh 最后调用到AudioPolicyManager方法中
            status = AudioSystem::startOutput(track->portId());
        // lyh 加入到活跃Track的数组中
        mActiveTracks.add(track);

    }
    // lyh 通过广播唤醒PlaybackThread.threadLoop()
    onAddNewTrack_l();
    return status;
}

复制代码

猜你喜欢

转载自juejin.im/post/7106418810080460808