[audio] AudioTrack (五) 共享内存创建分析

在创建 AudioTrack 有一个变量需要设置

MODE_STATIC 一整段 PCM,一次性给 AudioFlinger,之后只读不写

MODE_STREAM 是用来播放一个音频流

Kotlin 复制代码
.setTransferMode(AudioTrack.MODE_STREAM)

public static final int MODE_STATIC = 0;

public static final int MODE_STREAM = 1;

base/core/jni/android_media_AudioTrack.cpp android_media_AudioTrack_setup

audio_has_proportional_frames 用来判断音频是不是 pcm 线性关系

计算出总 frameCount

重点看下这个memoryMode参数

如果参数是 MODE_STREAM shared mem 参数就是 0

如果参数是 MODE_STATIC 那直接在app层申请内存 allocSharedMem

调用lpTrack->set设置下去

cpp 复制代码
static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
                                           jobject jaa, jintArray jSampleRate,
                                           jint channelPositionMask, jint channelIndexMask,
                                           jint audioFormat, jint buffSizeInBytes, jint memoryMode,
                                           jintArray jSession, jobject jAttributionSource,
                                           jlong nativeAudioTrack, jboolean offload,
                                           jint encapsulationMode, jobject tunerConfiguration,
                                           jstring opPackageName) {


        // compute the frame count
        size_t frameCount;
        if (audio_has_proportional_frames(format)) {
            const size_t bytesPerSample = audio_bytes_per_sample(format);
            frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
        } else {
            frameCount = buffSizeInBytes;
        }


switch (memoryMode) {
    case MODE_STREAM:
        status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed
                                                    // in paa (last argument)
                                sampleRateInHertz,
                                format, // word length, PCM
                                nativeChannelMask, offload ? 0 : frameCount,
                                offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD
                                        : AUDIO_OUTPUT_FLAG_NONE,
                                lpJniStorage,
                                0,    // notificationFrames == 0 since not using EVENT_MORE_DATA
                                    // to feed the AudioTrack
                                0,    // shared mem
                                true, // thread can call Java
                                sessionId, // audio session ID
                                offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK
                                        : AudioTrack::TRANSFER_SYNC,
                                (offload || encapsulationMode) ? &offloadInfo : NULL,
                                attributionSource, // Passed from Java
                                paa.get());
        break;

    case MODE_STATIC:
    {
        // AudioTrack is using shared memory
        const auto iMem = allocSharedMem(buffSizeInBytes);

        status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed
                                                    // in paa (last argument)
                                sampleRateInHertz,
                                format, // word length, PCM
                                nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE,
                                lpJniStorage,
                                0,    // notificationFrames == 0 since not using EVENT_MORE_DATA
                                    // to feed the AudioTrack
                                iMem, // shared mem
                                true, // thread can call Java
                                sessionId, // audio session ID
                                AudioTrack::TRANSFER_SHARED,
                                nullptr,           // default offloadInfo
                                attributionSource, // Passed from Java
                                paa.get());
        break;
    }
    default:
        ALOGE("Unknown mode %d", memoryMode);
        goto native_init_failure;
    }


    return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}


sp<IMemory> allocSharedMem(int sizeInBytes) {
    const auto heap = sp<MemoryHeapBase>::make(sizeInBytes, 0, "AudioTrack Heap Base");
    if (heap->getBase() == MAP_FAILED || heap->getBase() == nullptr) {
        return nullptr;
    }
    return sp<MemoryBase>::make(heap, 0, sizeInBytes);
}

av/media/libaudioclient/AudioTrack.cpp lpTrack->set

sharedBuffer

主要就看这个函数, 使用返回的 response 解析出 Cblk

STREAM 模式下音频数据 buffers 就是 + 1

STATIC 模式下音频数据 buffers 就是传进来的 buffer

cpp 复制代码
status_t AudioTrack::set(
...
        const sp<IMemory>& sharedBuffer,
...)
{

    mSharedBuffer = sharedBuffer;

    {
        AutoMutex lock(mLock);
        status = createTrack_l();
    }

    return logIfErrorAndReturnStatus(status, "");
}


status_t AudioTrack::createTrack_l()
{
    status_t status;

    const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();

    IAudioFlinger::CreateTrackInput input;

    input.sharedBuffer = mSharedBuffer;

    auto aidlInput = input.toAidl();
    
    // 使用返回的 response 解析出 Cblk
    status = audioFlinger->createTrack(aidlInput.value(), response);

    IAudioFlinger::CreateTrackOutput output{};
    
    auto trackOutput = IAudioFlinger::CreateTrackOutput::fromAidl(response);

    output = trackOutput.value();
    
    output.audioTrack->getCblk(&sfr);

    auto iMemory = aidl2legacy_NullableSharedFileRegion_IMemory(sfr);

    sp<IMemory> iMem = iMemory.value();

    void *iMemPointer = iMem->unsecurePointer();

    audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);

    void* buffers;
    if (mSharedBuffer == 0) {
        
        // STREAM 模式下音频数据 buffers 就是 + 1
        buffers = cblk + 1;
    } else {
        // STATIC 模式下音频数据 buffers 就是传进来的 buffer
        buffers = mSharedBuffer->unsecurePointer();

        }
    }
    

    return logIfErrorAndReturnStatus(status, "");
}

audioflinger

av/services/audioflinger/AudioFlinger.cpp createTrack

input.sharedBuffer

cpp 复制代码
status_t AudioFlinger::createTrack(const media::CreateTrackRequest& _input,
                                   media::CreateTrackResponse& _output)
{
    CreateTrackInput input = VALUE_OR_RETURN_STATUS(CreateTrackInput::fromAidl(_input));

    track = thread->createTrack_l(..., input.sharedBuffer, ...);

    return lStatus;
}

av/services/audioflinger/Threads.cpp createTrack_l

const sp<IMemory>& sharedBuffer

cpp 复制代码
sp<IAfTrack> PlaybackThread::createTrack_l(...,
                    const sp<IMemory>& sharedBuffer, ...)
{
    
    //检测内存是否正常
    if (sharedBuffer != 0 && checkIMemory(sharedBuffer) != NO_ERROR) {
        lStatus = BAD_VALUE;
        goto Exit;
    }

    track = IAfTrack::create(this, client, streamType, attr, sampleRate, format,
                        channelMask, frameCount,
                        nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
                        sessionId, creatorPid, attributionSource, trackFlags,
                        IAfTrackBase::TYPE_DEFAULT, portId, SIZE_MAX /*frameCountToBeReady*/,
                        speed, isSpatialized, isBitPerfect, volume, muted);

    return track;
}

av/services/audioflinger/Tracks.cpp

cpp 复制代码
sp<IAfTrack> IAfTrack::create(...,  const sp<IMemory>& sharedBuffer, ...) 
{
    return sp<Track>::make(thread, ..., sharedBuffer, ...);
}

av/services/audioflinger/Tracks.cpp

在初始化父类构造器的时候,alloc 值为 ALLOC_CBLK

cpp 复制代码
Track::Track(... , const sp<IMemory>& sharedBuffer, ...)
    :   TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount,
(sharedBuffer != 0) ? sharedBuffer->unsecurePointer() : buffer,
(sharedBuffer != 0) ? sharedBuffer->size() : bufferSize,
... ,
ALLOC_CBLK, /*alloc*/
...),


    mFillingStatus(FS_INVALID),
    // mRetryCount initialized later when needed
    mSharedBuffer(sharedBuffer),
{


}

// native/libs/binder/IMemory.cpp

void* IMemory::unsecurePointer() const {
    ssize_t offset;
    sp<IMemoryHeap> heap = getMemory(&offset);
    void* const base = heap!=nullptr ? heap->base() : MAP_FAILED;
    if (base == MAP_FAILED)
        return nullptr;
    return static_cast<char*>(base) + offset;
}

size_t IMemory::size() const {
    size_t size;
    getMemory(nullptr, &size);
    return size;
}

MODE_STATIC: buffer != NULL

size = sizeof(audio_track_cblk_t);

mCblk = allocator.allocate(size);

mBuffer = buffer;

MODE_STREAM: buffer == NULL

size = sizeof(audio_track_cblk_t) + bufferSize;

mCblk = allocator.allocate(size);

内存长这样

| audio_track_cblk_t | PCM ring buffer |

mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);

cpp 复制代码
TrackBase::TrackBase(... ,
            void *buffer,
            size_t bufferSize, ...)
{

    size_t minBufferSize = buffer == NULL ? roundup(frameCount) : frameCount;

    if (minBufferSize < frameCount  // roundup rounds down for values above UINT_MAX / 2
            || mFrameSize == 0   // format needs to be correct
            || minBufferSize > SIZE_MAX / mFrameSize) {
        android_errorWriteLog(0x534e4554, "34749571");
        return;
    }
    minBufferSize *= mFrameSize;

    if (buffer == nullptr) {
        bufferSize = minBufferSize; // allocated here.
    } else if (minBufferSize > bufferSize) {
        android_errorWriteLog(0x534e4554, "38340117");
        return;
    }

    size_t size = sizeof(audio_track_cblk_t);
    if (buffer == NULL && alloc == ALLOC_CBLK) {
        // check overflow when computing allocation size for streaming tracks.
        if (size > SIZE_MAX - bufferSize) {
            android_errorWriteLog(0x534e4554, "34749571");
            return;
        }
        size += bufferSize;
    }

    if (client != 0) {
        mCblkMemory = client->allocator().allocate(mediautils::NamedAllocRequest{{size},
                std::string("Track ID: ").append(std::to_string(mId))});
        if (mCblkMemory == 0 ||
                (mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->unsecurePointer())) == NULL) {
            ALOGE("%s(%d): not enough memory for AudioTrack size=%zu", __func__, mId, size);
            ALOGE("%s", client->allocator().dump().c_str());
            mCblkMemory.clear();
            return;
        }
    } else {
//...
    }

    if (mCblk != NULL) {
        new(mCblk) audio_track_cblk_t();
        switch (alloc) {

//...
        case ALLOC_CBLK:
            // clear all buffers
            if (buffer == NULL) {
                mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
                memset(mBuffer, 0, bufferSize);
            } else {
                mBuffer = buffer;
            }
            break;
//...
        case ALLOC_NONE:
            mBuffer = buffer;
            break;
    
}
相关推荐
一 乐2 小时前
景区管理|基于springboot + vue景区管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习
幽络源小助理2 小时前
SpringBoot+Vue大型商场应急预案管理系统源码 | Java安全类项目免费下载 – 幽络源
java·vue.js·spring boot
lbb 小魔仙2 小时前
【Java】Spring Boot 与 Spring Cloud 整合:微服务架构入门实战
java·spring boot·spring cloud·架构
傻啦嘿哟2 小时前
实战:用GraphQL接口高效采集数据
开发语言·驱动开发·php
JIngJaneIL2 小时前
基于java + vue连锁门店管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
hslinux2 小时前
NDK 通过configure 编译C++源码通用脚本
android·c++·ndk·configure
秃了也弱了。2 小时前
python实现离线文字转语音:pyttsx3 库
开发语言·python
superman超哥2 小时前
Rust 减少内存分配策略:性能优化的内存管理艺术
开发语言·后端·性能优化·rust·内存管理·内存分配策略
月明长歌2 小时前
怎么把 SQL 的增删改查写成“稳、准、可维护”的
java·数据库·sql