Android13 SoundPool play流程分析

SoundPool的play方法用于播放音效文件,代码如下:

//frameworks/base/media/java/android/media/SoundPool.java
public class SoundPool extends PlayerBase {
    public final int play(int soundID, float leftVolume, float rightVolume,
            int priority, int loop, float rate) {
        // FIXME: b/174876164 implement device id for soundpool
        baseStart(0);
        return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
    }
}

调用_play方法,_play是一个native方法,实现代码如下:

//frameworks/base/media/jni/soundpool/android_media_soundPool.cpp
static jint
android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
        jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
        jfloat rate)
{
    ALOGV("android_media_SoundPool_play\n");
    auto soundPool = getSoundPool(env, thiz);
    if (soundPool == nullptr) return 0;
    return (jint) soundPool->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
}

上面方法处理如下:

1、调用getSoundPool方法,获取SoundPool对象

2、调用SoundPool的play方法进行音效声音play

下面分别进行分析:

getSoundPool

调用getSoundPool方法,获取SoundPool对象:

//frameworks/base/media/jni/soundpool/android_media_soundPool.cpp
inline auto getSoundPool(JNIEnv *env, jobject thiz) {
    return getSoundPoolManager().get(env, thiz);
}

调用getSoundPoolManager方法:

//frameworks/base/media/jni/soundpool/android_media_soundPool.cpp
auto& getSoundPoolManager() {
    static ObjectManager<std::shared_ptr<SoundPool>> soundPoolManager(fields.mNativeContext);
    return soundPoolManager;
}

SoundPool play

调用SoundPool的play方法进行音效声音play:

//frameworks/base/media/jni/SoundPool/SoundPool.cpp
int32_t SoundPool::play(int32_t soundID, float leftVolume, float rightVolume,
        int32_t priority, int32_t loop, float rate)
{
    ALOGV("%s(soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)",
            __func__, soundID, leftVolume, rightVolume, priority, loop, rate);


    // New for R: check arguments to ensure track can be created.
    // If SoundPool defers the creation of the AudioTrack to the StreamManager thread,
    // the failure to create may not be visible to the caller, so this precheck is needed.
    if (checkVolume(&leftVolume, &rightVolume)
            || checkPriority(&priority)
            || checkLoop(&loop)
            || checkRate(&rate)) return 0;


    auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
    const std::shared_ptr<soundpool::Sound> sound = mSoundManager.findSound(soundID); //通过soundID获取sound 对象
    if (sound == nullptr || sound->getState() != soundpool::Sound::READY) { //判断sound状态
        ALOGW("%s soundID %d not READY", __func__, soundID);
        return 0;
    }


    const int32_t streamID = mStreamManager.queueForPlay(
            sound, soundID, leftVolume, rightVolume, priority, loop, rate);
    ALOGV("%s returned %d", __func__, streamID);
    return streamID;
}

调用StreamManager的queueForPlay方法:

//frameworks/base/media/jni/SoundPool/StreamManager.cpp
int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound,
        int32_t soundID, float leftVolume, float rightVolume,
        int32_t priority, int32_t loop, float rate)
{
    ALOGV("%s(sound=%p, soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)",
            __func__, sound.get(), soundID, leftVolume, rightVolume, priority, loop, rate);
    bool launchThread = false;
    int32_t streamID = 0;
    std::vector<std::any> garbage;


    { // for lock
        std::unique_lock lock(mStreamManagerLock);
        Stream *newStream = nullptr;
        bool fromAvailableQueue = false;
        ALOGV("%s: mStreamManagerLock lock acquired", __func__);


        sanityCheckQueue_l();
        // find an available stream, prefer one that has matching sound id.
        if (mAvailableStreams.size() > 0) {
            for (auto stream : mAvailableStreams) {
                if (stream->getSoundID() == soundID) {
                    newStream = stream;
                    ALOGV("%s: found soundID %d in available queue", __func__, soundID);
                    break;
                }
            }
            if (newStream == nullptr) {
                ALOGV("%s: found stream in available queue", __func__);
                newStream = *mAvailableStreams.begin();
            }
            newStream->setStopTimeNs(systemTime());
            fromAvailableQueue = true;
        }


        // also look in the streams restarting (if the paired stream doesn't have a pending play)
        if (newStream == nullptr || newStream->getSoundID() != soundID) {
            for (auto [unused , stream] : mRestartStreams) {
                if (!stream->getPairStream()->hasSound()) {
                    if (stream->getSoundID() == soundID) {
                        ALOGV("%s: found soundID %d in restart queue", __func__, soundID);
                        newStream = stream;
                        fromAvailableQueue = false;
                        break;
                    } else if (newStream == nullptr) {
                        ALOGV("%s: found stream in restart queue", __func__);
                        newStream = stream;
                    }
                }
            }
        }


        // no available streams, look for one to steal from the active list
        if (newStream == nullptr) {
            for (auto stream : mActiveStreams) {
                if (stream->getPriority() <= priority) {
                    if (newStream == nullptr
                            || newStream->getPriority() > stream->getPriority()) {
                        newStream = stream;
                        ALOGV("%s: found stream in active queue", __func__);
                    }
                }
            }
            if (newStream != nullptr) { // we need to mute as it is still playing.
                (void)newStream->requestStop(newStream->getStreamID());
            }
        }


        // none found, look for a stream that is restarting, evict one.
        if (newStream == nullptr) {
            for (auto [unused, stream] : mRestartStreams) {
                if (stream->getPairPriority() <= priority) {
                    ALOGV("%s: evict stream from restart queue", __func__);
                    newStream = stream;
                    break;
                }
            }
        }


        // DO NOT LOOK into mProcessingStreams as those are held by the StreamManager threads.


        if (newStream == nullptr) {
            ALOGD("%s: unable to find stream, returning 0", __func__);
            return 0; // unable to find available stream
        }


        Stream *pairStream = newStream->getPairStream();
        streamID = getNextIdForStream(pairStream);
        ALOGV("%s: newStream:%p  pairStream:%p, streamID:%d",
                __func__, newStream, pairStream, streamID);
        pairStream->setPlay(
                streamID, sound, soundID, leftVolume, rightVolume, priority, loop, rate);
        if (fromAvailableQueue && kPlayOnCallingThread) {
            removeFromQueues_l(newStream);
            mProcessingStreams.emplace(newStream);
            lock.unlock();
            if (Stream* nextStream = newStream->playPairStream(garbage)) {
                lock.lock();
                ALOGV("%s: starting streamID:%d", __func__, nextStream->getStreamID());
                addToActiveQueue_l(nextStream);
            } else {
                lock.lock();
                mAvailableStreams.insert(newStream);
                streamID = 0;
            }
            mProcessingStreams.erase(newStream);
        } else {
            launchThread = moveToRestartQueue_l(newStream) && needMoreThreads_l();
        }
        sanityCheckQueue_l();
        ALOGV("%s: mStreamManagerLock released", __func__);
    } // lock


    if (launchThread) {
        const int32_t id = mThreadPool->launch([this](int32_t id) { run(id); });
        (void)id; // avoid clang warning -Wunused-variable -Wused-but-marked-unused
        ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
    }
    ALOGV("%s: returning %d", __func__, streamID);
    // garbage is cleared here outside mStreamManagerLock.
    return streamID;
}

上面主要处理如下:

1、调用Stream的setPlay方法,设置Play相关变量。

2、调用playPairStream方法,播放配对流。

下面分别进行分析:

Stream setPlay

调用Stream的setPlay方法,设置Play相关变量:

//frameworks/base/media/jni/SoundPool/Stream.cpp
void Stream::setPlay(
        int32_t streamID, const std::shared_ptr<Sound> &sound, int32_t soundID,
        float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate)
{
    std::lock_guard lock(mLock);
    // We must be idle, or we must be repurposing a pending Stream.
    LOG_ALWAYS_FATAL_IF(mState != IDLE && mAudioTrack != nullptr, "State %d must be IDLE", mState);
    mSound = sound;
    mSoundID = soundID;
    mLeftVolume = leftVolume;
    mRightVolume = rightVolume;
    mPriority = priority;
    mLoop = loop;
    mRate = rate;
    mState = PLAYING;
    mAutoPaused = false;   // New for R (consistent with Java API spec).
    mStreamID = streamID;  // prefer this to be the last, as it is an atomic sync point
}

Stream playPairStream

调用playPairStream方法,播放配对流:

//frameworks/base/media/jni/SoundPool/Stream.cpp
Stream* Stream::playPairStream(std::vector<std::any>& garbage) {
    Stream* pairStream = getPairStream();
    LOG_ALWAYS_FATAL_IF(pairStream == nullptr, "No pair stream!");
    {
        ALOGV("%s: track streamID: %d", __func__, (int)getStreamID());
        // TODO: Do we really want to force a simultaneous synchronization between
        // the stream and its pair?


        // note locking order - the paired stream is obtained before the queued stream.
        // we can invert the locking order, but it is slightly more optimal to do it this way.
        std::lock_guard lockp(pairStream->mLock);
        if (pairStream->mSound == nullptr) {
            return nullptr; // no pair sound
        }
        {
            std::lock_guard lock(mLock);
            LOG_ALWAYS_FATAL_IF(mState != IDLE, "State: %d must be IDLE", mState);
            // TODO: do we want a specific set() here?
            pairStream->mAudioTrack = mAudioTrack;
            pairStream->mSoundID = mSoundID; // optimization to reuse AudioTrack.
            pairStream->mToggle = mToggle;
            pairStream->mAutoPaused = mAutoPaused; // save autopause state
            pairStream->mMuted = mMuted;
            mAudioTrack.clear();  // the pair owns the audiotrack.
            mSound.reset();
            mSoundID = 0;
        }
        // TODO: do we need a specific play_l() anymore?
        const int pairState = pairStream->mState;
        pairStream->play_l(pairStream->mSound, pairStream->mStreamID,
                pairStream->mLeftVolume, pairStream->mRightVolume, pairStream->mPriority,
                pairStream->mLoop, pairStream->mRate, garbage);
        if (pairStream->mState == IDLE) {
            return nullptr; // AudioTrack error
        }
        if (pairState == PAUSED) {  // reestablish pause
            pairStream->mState = PAUSED;
            pairStream->mAudioTrack->pause();
        }
    }
    return pairStream;
}

调用Stream的play_l方法:

//frameworks/base/media/jni/SoundPool/Stream.cpp
sp<AudioTrack>      mAudioTrack GUARDED_BY(mLock);
void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
        float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
        std::vector<std::any>& garbage)
{
    ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
            " priority=%d, loop=%d, rate=%f)",
            __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
            priority, loop, rate);


    // initialize track
    const audio_stream_type_t streamType =
            AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
    const int32_t channelCount = sound->getChannelCount();
    const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
    size_t frameCount = 0;


    if (loop) {
        const audio_format_t format = sound->getFormat();
        const size_t frameSize = audio_is_linear_pcm(format)
                ? channelCount * audio_bytes_per_sample(format) : 1;
        frameCount = sound->getSizeInBytes() / frameSize;
    }


    if (mAudioTrack != nullptr) {
        if (mSoundID == sound->getSoundID()
                && mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) { //调用AudioTrack设置波特率
            // Reuse the old track if the soundID matches.
            // the sample rate may fail to change if the audio track is a fast track.
            ALOGV("%s: reusing track %p for sound %d",
                    __func__, mAudioTrack.get(), sound->getSoundID());
        } else {
            // If reuse not possible, move mAudioTrack to garbage, set to nullptr.
            garbage.emplace_back(std::move(mAudioTrack));
            mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
        }
    }
    if (mAudioTrack == nullptr) {
        // mToggle toggles each time a track is started on a given stream.
        // This enables the detection of callbacks received from the old
        // audio track while the new one is being started and avoids processing them with
        // wrong audio audio buffer size  (mAudioBufferSize)
        auto toggle = mToggle ^ 1;
        // NOLINTNEXTLINE(performance-no-int-to-ptr)
        audio_channel_mask_t soundChannelMask = sound->getChannelMask();
        // When sound contains a valid channel mask, use it as is.
        // Otherwise, use stream count to calculate channel mask.
        audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
                ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);


        // do not create a new audio track if current track is compatible with sound parameters


        android::content::AttributionSourceState attributionSource;
        attributionSource.packageName = mStreamManager->getOpPackageName();
        attributionSource.token = sp<BBinder>::make();
        mCallback =  sp<StreamCallback>::make(this, toggle),
        // TODO b/182469354 make consistent with AudioRecord, add util for native source
        mAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
                channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
                mCallback,
                0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
                AudioTrack::TRANSFER_DEFAULT,
                nullptr /*offloadInfo*/, attributionSource,
                mStreamManager->getAttributes(),
                false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/); //创建AudioTrack
        // Set caller name so it can be logged in destructor.
        // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
        mAudioTrack->setCallerName("soundpool");


        if (status_t status = mAudioTrack->initCheck();
            status != NO_ERROR) {
            ALOGE("%s: error %d creating AudioTrack", __func__, status);
            // TODO: should we consider keeping the soundID and reusing the old track?
            mState = IDLE;
            mSoundID = 0;
            mSound.reset();
            garbage.emplace_back(std::move(mAudioTrack)); // remove mAudioTrack.
            mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
            return;
        }
        // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
        mToggle = toggle;
        ALOGV("%s: using new track %p for sound %d",
                __func__, mAudioTrack.get(), sound->getSoundID());
    }
    if (mMuted) {
        mAudioTrack->setVolume(0.f, 0.f); //设置AudioTrack Volume
    } else {
        mAudioTrack->setVolume(leftVolume, rightVolume);
    }
    mAudioTrack->setLoop(0, frameCount, loop); //设置AudioTrack Loop
    mAudioTrack->start(); //start AudioTrack播放
    mSound = sound;
    mSoundID = sound->getSoundID();
    mPriority = priority;
    mLoop = loop;
    mLeftVolume = leftVolume;
    mRightVolume = rightVolume;
    mRate = rate;
    mState = PLAYING;
    mStopTimeNs = 0;
    mStreamID = nextStreamID;  // prefer this to be the last, as it is an atomic sync point
}

上面方法主要处理如下:

1、创建AudioTrack对象

2、调用AudioTrack的setSampleRate方法设置播放波特率

3、调用AudioTrack的setVolume方法,设置Track音量

4、调用AudioTrack的setLoop方法,设置音频循环播放

5、调用AudioTrack的start方法,播放Track

下面分别进行分析:

new AudioTrack

待更新

AudioTrack start

待更新

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值