Android13 SoundPool 创建流程分析

SoundPool用于播放密集,急促而又短暂的音效,如案件音,SoundPool的创建方式有两种:

1、通过new的方式直接创建SoundPool对象,如:

SoundPool mSoundPool = new SoundPool(1, AudioManager.STREAM_RING, 0);

SoundPool的构造方法如下:

//frameworks/base/media/java/android/media/SoundPool.java
public class SoundPool extends PlayerBase {
    public SoundPool(int maxStreams, int streamType, int srcQuality) {
        this(maxStreams,
                new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build());
        PlayerBase.deprecateStreamTypeForPlayback(streamType, "SoundPool", "SoundPool()");
    }
}

2、通过new一个SoundPool的构造器创建,如:

SoundPool   mSoundPool = new SoundPool.Builder()
                .setMaxStreams(NUM_SOUNDPOOL_CHANNELS)
                .setAudioAttributes(new AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                        .build())
                .build();

SoundPool的构造器如下:

//frameworks/base/media/java/android/media/SoundPool.java
public class SoundPool extends PlayerBase {
    public static class Builder {
        private int mMaxStreams = 1;
        private AudioAttributes mAudioAttributes;


        /**
         * Constructs a new Builder with the defaults format values.
         * If not provided, the maximum number of streams is 1 (see {@link #setMaxStreams(int)} to
         * change it), and the audio attributes have a usage value of
         * {@link AudioAttributes#USAGE_MEDIA} (see {@link #setAudioAttributes(AudioAttributes)} to
         * change them).
         */
        public Builder() {
        }


        /**
         * Sets the maximum of number of simultaneous streams that can be played simultaneously.
         * @param maxStreams a value equal to 1 or greater.
         * @return the same Builder instance
         * @throws IllegalArgumentException
         */
        public Builder setMaxStreams(int maxStreams) throws IllegalArgumentException {
            if (maxStreams <= 0) {
                throw new IllegalArgumentException(
                        "Strictly positive value required for the maximum number of streams");
            }
            mMaxStreams = maxStreams;
            return this;
        }


        /**
         * Sets the {@link AudioAttributes}. For examples, game applications will use attributes
         * built with usage information set to {@link AudioAttributes#USAGE_GAME}.
         * @param attributes a non-null
         * @return
         */
        public Builder setAudioAttributes(AudioAttributes attributes)
                throws IllegalArgumentException {
            if (attributes == null) {
                throw new IllegalArgumentException("Invalid null AudioAttributes");
            }
            mAudioAttributes = attributes;
            return this;
        }


        public SoundPool build() {
            if (mAudioAttributes == null) {
                mAudioAttributes = new AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_MEDIA).build();
            }
            return new SoundPool(mMaxStreams, mAudioAttributes);
        }
    }
}

Builder的build方法主要处理如下:

1、通过AudioAttributes的构造器,创建AudioAttributes对象。

2、通过new的方式创建SoundPool对象。

下面分别进行分析:

AudioAttributes Builder

通过AudioAttributes的构造器,创建AudioAttributes对象:

//frameworks/base/media/java/android/media/AudioAttributes.java
public final class AudioAttributes implements Parcelable {
    public static class Builder {
        public AudioAttributes build() {
            AudioAttributes aa = new AudioAttributes();
            aa.mContentType = mContentType;


            if (mUsage == USAGE_INVALID) {
                if (mSystemUsage == USAGE_INVALID) {
                    aa.mUsage = USAGE_UNKNOWN;
                } else {
                    aa.mUsage = mSystemUsage;
                }
            } else {
                if (mSystemUsage == USAGE_INVALID) {
                    aa.mUsage = mUsage;
                } else {
                    throw new IllegalArgumentException(
                            "Cannot set both usage and system usage on same builder");
                }
            }


            // handle deprecation of notification usages by remapping to USAGE_NOTIFICATION
            switch (aa.mUsage) {
                case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
                case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
                case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
                    aa.mUsage = USAGE_NOTIFICATION;
                    break;
                default:
                    break;
            }


            aa.mSource = mSource;
            aa.mFlags = mFlags;
            if (mMuteHapticChannels) {
                aa.mFlags |= FLAG_MUTE_HAPTIC;
            }
            if (mIsContentSpatialized) {
                aa.mFlags |= FLAG_CONTENT_SPATIALIZED;
            }
            if (mSpatializationBehavior == SPATIALIZATION_BEHAVIOR_NEVER) {
                aa.mFlags |= FLAG_NEVER_SPATIALIZE;
            }


            if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) {
                // capturing for camcorder or communication is private by default to
                // reflect legacy behavior
                if (mSource == MediaRecorder.AudioSource.VOICE_COMMUNICATION
                        || mSource == MediaRecorder.AudioSource.CAMCORDER) {
                    aa.mFlags |= FLAG_CAPTURE_PRIVATE;
                } else {
                    aa.mFlags &= ~FLAG_CAPTURE_PRIVATE;
                }
            } else if (mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED) {
                aa.mFlags |= FLAG_CAPTURE_PRIVATE;
            } else {
                aa.mFlags &= ~FLAG_CAPTURE_PRIVATE;
            }
            aa.mTags = (HashSet<String>) mTags.clone();
            aa.mFormattedTags = TextUtils.join(";", mTags);
            if (mBundle != null) {
                aa.mBundle = new Bundle(mBundle);
            }


            // Allow the FLAG_HW_HOTWORD only for AudioSource.VOICE_RECOGNITION
            if (mSource != MediaRecorder.AudioSource.VOICE_RECOGNITION
                    && (mFlags & FLAG_HW_HOTWORD) == FLAG_HW_HOTWORD) {
                aa.mFlags &= ~FLAG_HW_HOTWORD;
            }
            return aa;
        }
    }
}

new SoundPool

SoundPool的构造方法:

//frameworks/base/media/java/android/media/SoundPool.java
public class SoundPool extends PlayerBase {
    private SoundPool(int maxStreams, AudioAttributes attributes) {
        super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL); //调用父类PlayerBase的构造方法


        // do native setup
        if (native_setup(maxStreams, attributes, getCurrentOpPackageName()) != 0) { //调用native方法
            throw new RuntimeException("Native setup failed");
        }
        mAttributes = attributes;


        // FIXME: b/174876164 implement session id for soundpool
        baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE); //调用父类的baseRegisterPlayer方法
    }
}

上面方法主要处理如下:

1、调用父类PlayerBase的构造方法

2、调用native方法native_setup

3、调用父类的baseRegisterPlayer方法

4、调用ConcurrentHashMap的set方法

下面分别进行分析:

PlayerBase

PlayerBase的构造方法:

//frameworks/base/media/java/android/media/PlayerBase.java
public abstract class PlayerBase {
    PlayerBase(@NonNull AudioAttributes attr, int implType) {
        if (attr == null) {
            throw new IllegalArgumentException("Illegal null AudioAttributes");
        }
        mAttributes = attr;
        mImplType = implType;
        mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE;
    };
}

native_setup

调用native方法native_setup,通过查询调用的是android_media_SoundPool_native_setup方法:

//frameworks/base/media/jni/soundpool/android_media_SoundPool.cpp
static jint android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz,
        jint maxChannels, jobject jaa, jstring opPackageName)
{
    ALOGV("android_media_SoundPool_native_setup");
    if (jaa == nullptr) {
        ALOGE("Error creating SoundPool: invalid audio attributes");
        return -1;
    }


    // Use the AUDIO_ATTRIBUTES_INITIALIZER here to ensure all non-relevant fields are
    // initialized properly. (note that .source is not explicitly initialized here).
    audio_attributes_t audioAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
    // read the AudioAttributes values
    const auto jtags =
            (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
    const char* tags = env->GetStringUTFChars(jtags, nullptr);
    // infers array size and guarantees zero termination (does not zero fill to the end).
    audio_utils_strlcpy(audioAttributes.tags, tags);
    env->ReleaseStringUTFChars(jtags, tags);
    audioAttributes.usage =
            (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
    audioAttributes.content_type =
            (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
    audioAttributes.flags =
            (audio_flags_mask_t) env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
    ScopedUtfChars opPackageNameStr(env, opPackageName);
    auto soundPool = std::make_shared<SoundPool>( 
            maxChannels, audioAttributes, opPackageNameStr.c_str()); //创建soundPool 类型的std::shared_ptr智能指针
    soundPool->setCallback(android_media_callback, nullptr /* user */); //调用soundPool的setCallback方法,设置soundPool的Callback


    // register with SoundPoolManager.
    // 注册 SoundPoolManager
    auto oldSoundPool = setSoundPool(env, thiz, soundPool);
    // register Java SoundPool WeakRef using native SoundPool * as the key, for the callback.
    // 使用原生 SoundPool 作为回调的密钥注册 Java SoundPool WeakRef
    auto oldSoundPoolJavaRef = getSoundPoolJavaRefManager().set(
            soundPool.get(), make_shared_weakglobalref_from_localref(env, thiz)); 


    ALOGW_IF(oldSoundPool != nullptr, "%s: Aliased SoundPool object %p",
            __func__, oldSoundPool.get());
    return 0;
}

上述方法主要处理如下:

1、创建soundPool类型的std::shared_ptr智能指针,会调用soundPool 的构造方法。

2、调用soundPool的setCallback方法,设置soundPool的Callback。

3、调用setSoundPool方法

4、调用getSoundPoolJavaRefManager方法

下面分别进行分析:

SoundPool::SoundPool

创建soundPool类型的std::shared_ptr智能指针,会调用soundPool 的构造方法:

//frameworks/base/media/jni/SoundPool/SoundPool.cpp
soundpool::StreamManager mStreamManager;
SoundPool::SoundPool(
        int32_t maxStreams, const audio_attributes_t& attributes,
        const std::string& opPackageName)
    : mStreamManager(maxStreams, kStreamManagerThreads, attributes, opPackageName)
{
    ALOGV("%s(maxStreams=%d, attr={ content_type=%d, usage=%d, flags=0x%x, tags=%s })",
            __func__, maxStreams,
            attributes.content_type, attributes.usage, attributes.flags, attributes.tags);
}

创建StreamManager对象,StreamManager的构造方法:

//frameworks/base/media/jni/SoundPool/StreamManager.cpp
StreamManager::StreamManager(
        int32_t streams, size_t threads, const audio_attributes_t& attributes,
        std::string opPackageName)
    : StreamMap(streams)
    , mAttributes(attributes)
    , mOpPackageName(std::move(opPackageName))
    , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop)
{
    ALOGV("%s(%d, %zu, ...)", __func__, streams, threads);
    forEach([this](Stream *stream) {
        stream->setStreamManager(this);
        if ((streamPosition(stream) & 1) == 0) { // put the first stream of pair as available.
            mAvailableStreams.insert(stream);
        }
    });


    mThreadPool = std::make_unique<ThreadPool>(
            std::min((size_t)streams,  // do not make more threads than streams to play
                    std::min(threads, (size_t)std::thread::hardware_concurrency())),
            "SoundPool_");
}
soundPool::setCallback

调用soundPool的setCallback方法,设置soundPool的Callback:

//frameworks/base/media/jni/SoundPool/SoundPool.cpp
soundpool::SoundManager  mSoundManager;
void SoundPool::setCallback(SoundPoolCallback* callback, void* user)
{
    ALOGV("%s(%p, %p)", __func__, callback, user);
    auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
    mSoundManager.setCallback(this, callback, user);
}

调用SoundManager的setCallback方法:

//frameworks/base/media/jni/SoundPool/SoundManager.cpp
CallbackHandler mCallbackHandler;  
void SoundManager::setCallback(SoundPool *soundPool, SoundPoolCallback* callback, void* user)
{
    mCallbackHandler.setCallback(soundPool, callback, user);
}

调用CallbackHandler的setCallback方法:

//frameworks/base/media/jni/SoundPool/SoundManager.cpp
void setCallback(SoundPool *soundPool, SoundPoolCallback* callback, void* userData)
{
    std::lock_guard<std::recursive_mutex> lock(mCallbackLock);
    mSoundPool = soundPool;
    mCallback = callback;
    mUserData = userData;
}
setSoundPool

调用setSoundPool方法:

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


inline auto setSoundPool(
        JNIEnv *env, jobject thiz, const std::shared_ptr<SoundPool>& soundPool) {
    return getSoundPoolManager().set(env, thiz, soundPool);
}

调用ObjectManager的set方法:

//frameworks/base/media/jni/soundpool/android_media_SoundPool.cpp
class ObjectManager
{
    T set(JNIEnv *env, jobject thiz, const T& newObject) {
        std::lock_guard lg(mLock);
        // NOLINTNEXTLINE(performance-no-int-to-ptr)
        auto ptr = reinterpret_cast<T*>(env->GetLongField(thiz, mFieldId));
        if (ptr != nullptr) {
            T old = std::move(*ptr);  // *ptr will be replaced or deleted.
            if (newObject) {
                env->SetLongField(thiz, mFieldId, (jlong)0);
                delete ptr;
                --mObjectCount;
            } else {
                *ptr = newObject;
            }
            return old;
        } else {
             if (newObject) {
                 env->SetLongField(thiz, mFieldId, (jlong)new T(newObject));
                 ++mObjectCount;
             }
             return {};
        }
    }
}
ConcurrentHashMap set

auto oldSoundPoolJavaRef = getSoundPoolJavaRefManager().set(soundPool.get(), make_shared_weakglobalref_from_localref(env, thiz));

通过getSoundPoolJavaRefManager方法获取ConcurrentHashMap:

//frameworks/base/media/jni/soundpool/android_media_SoundPool.cpp
auto& getSoundPoolJavaRefManager() {
    // Note this can store shared_ptrs to either jweak and jobject,
    // as the underlying type is identical.
    static ConcurrentHashMap<SoundPool *, std::shared_ptr<JWeakValue>> concurrentHashMap;
    return concurrentHashMap;
}

然后调用ConcurrentHashMap的set方法:

//frameworks/base/media/jni/soundpool/android_media_SoundPool.cpp
class ConcurrentHashMap
{
    V set(const K& key, const V& value) {
        std::lock_guard lg(mLock);
        auto it = mMap.find(key);
        if (it == mMap.end()) {
            if (value) {
                mMap[key] = value;
            }
            return {};
        }
        V oldValue = std::move(it->second);
        if (value) {
            it->second = value;
        } else {
            mMap.erase(it);
        }
        return oldValue;
    }
}

传入key和value,其中key通过soundPool的get方法获得获取soundPool指针本身,value是make_shared_weakglobalref_from_localref方法的返回值:

//frameworks/base/media/jni/soundpool/android_media_SoundPool.cpp
inline auto make_shared_weakglobalref_from_localref(JNIEnv *env, jobject localRef) {
    return std::shared_ptr<JWeakValue>(
            env->NewWeakGlobalRef(localRef), //创建一个新的弱全局引用
            [](JWeakValue* weak) { // cannot cache env as don't know which thread we're on.
                if (weak != nullptr) AndroidRuntime::getJNIEnv()->DeleteWeakGlobalRef(weak);
            });
}

PlayerBase baseRegisterPlayer

调用父类的baseRegisterPlayer方法:

//frameworks/base/media/java/android/media/PlayerBase.java
public abstract class PlayerBase {
    // 实例化/初始化成功时从派生类调用
    protected void baseRegisterPlayer(int sessionId) {
        try {
            mPlayerIId = getService().trackPlayer(
                    new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this),
                            sessionId));
        } catch (RemoteException e) {
            Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
        }
    }
}
  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值