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);
}
}
}