Refactor AudioTrack JNI stub
- Use new callback/buffer interface - Manage callback interface object lifetime with sp<> and ref-counting - Combine JNI storage into a ref-counted object, with lifetime tied to AudioTrack.java Test: atest AudioTrackTest Bug: 199156212 Bug: 216175830 Change-Id: I0ff9c90fa19f9f38287d827eaebdf58fa02aec5b
This commit is contained in:
@@ -63,12 +63,94 @@ static audio_track_fields_t javaAudioTrackFields;
|
||||
static PlaybackParams::fields_t gPlaybackParamsFields;
|
||||
static VolumeShaperHelper::fields_t gVolumeShaperFields;
|
||||
|
||||
struct audiotrack_callback_cookie {
|
||||
jclass audioTrack_class;
|
||||
jobject audioTrack_ref;
|
||||
bool busy;
|
||||
Condition cond;
|
||||
bool isOffload;
|
||||
class AudioTrackCallbackImpl : public AudioTrack::IAudioTrackCallback {
|
||||
public:
|
||||
enum event_type {
|
||||
// Keep in sync with java
|
||||
EVENT_MORE_DATA = 0,
|
||||
EVENT_UNDERRUN = 1,
|
||||
EVENT_LOOP_END = 2,
|
||||
EVENT_MARKER = 3,
|
||||
EVENT_NEW_POS = 4,
|
||||
EVENT_BUFFER_END = 5,
|
||||
EVENT_NEW_IAUDIOTRACK = 6,
|
||||
EVENT_STREAM_END = 7,
|
||||
// 8 is reserved for future use
|
||||
EVENT_CAN_WRITE_MORE_DATA = 9
|
||||
};
|
||||
|
||||
AudioTrackCallbackImpl(jclass audioTrackClass, jobject audioTrackWeakRef, bool isOffload)
|
||||
: mIsOffload(isOffload)
|
||||
{
|
||||
const auto env = getJNIEnv();
|
||||
mAudioTrackClass = (jclass)env->NewGlobalRef(audioTrackClass);
|
||||
// we use a weak reference so the AudioTrack object can be garbage collected.
|
||||
mAudioTrackWeakRef = env->NewGlobalRef(audioTrackWeakRef);
|
||||
|
||||
}
|
||||
|
||||
AudioTrackCallbackImpl(const AudioTrackCallbackImpl&) = delete;
|
||||
AudioTrackCallbackImpl& operator=(const AudioTrackCallbackImpl&) = delete;
|
||||
~AudioTrackCallbackImpl() {
|
||||
const auto env = getJNIEnv();
|
||||
env->DeleteGlobalRef(mAudioTrackClass);
|
||||
env->DeleteGlobalRef(mAudioTrackWeakRef);
|
||||
}
|
||||
|
||||
size_t onCanWriteMoreData(const AudioTrack::Buffer& buffer) override {
|
||||
if (!mIsOffload) {
|
||||
LOG_FATAL("Received canWrite callback for non-offload track");
|
||||
return 0;
|
||||
}
|
||||
const size_t availableForWrite = buffer.size();
|
||||
const int arg = availableForWrite > INT32_MAX ? INT32_MAX : (int) availableForWrite;
|
||||
postEvent(EVENT_CAN_WRITE_MORE_DATA, arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void onMarker([[maybe_unused]] uint32_t markerPosition) override {
|
||||
postEvent(EVENT_MARKER);
|
||||
}
|
||||
void onNewPos([[maybe_unused]] uint32_t newPos) override {
|
||||
postEvent(EVENT_NEW_POS);
|
||||
}
|
||||
|
||||
|
||||
void onNewIAudioTrack() override {
|
||||
if (!mIsOffload) return;
|
||||
postEvent(EVENT_NEW_IAUDIOTRACK);
|
||||
}
|
||||
|
||||
void onStreamEnd() override {
|
||||
if (!mIsOffload) return;
|
||||
postEvent(EVENT_STREAM_END);
|
||||
}
|
||||
|
||||
protected:
|
||||
jobject mAudioTrackWeakRef;
|
||||
private:
|
||||
JNIEnv* getJNIEnv() {
|
||||
auto jni = AndroidRuntime::getJNIEnv();
|
||||
if (jni == nullptr) {
|
||||
LOG_ALWAYS_FATAL("AudioTrackCallback thread JNI reference is null");
|
||||
}
|
||||
return jni;
|
||||
}
|
||||
|
||||
void postEvent(int event, int arg = 0) {
|
||||
auto env = getJNIEnv();
|
||||
env->CallStaticVoidMethod(
|
||||
mAudioTrackClass,
|
||||
javaAudioTrackFields.postNativeEventInJava,
|
||||
mAudioTrackWeakRef, event, arg, 0, NULL);
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
}
|
||||
}
|
||||
|
||||
jclass mAudioTrackClass;
|
||||
const bool mIsOffload;
|
||||
};
|
||||
|
||||
// keep these values in sync with AudioTrack.java
|
||||
@@ -76,22 +158,21 @@ struct audiotrack_callback_cookie {
|
||||
#define MODE_STREAM 1
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
class AudioTrackJniStorage {
|
||||
class AudioTrackJniStorage : public virtual RefBase,
|
||||
public AudioTrackCallbackImpl
|
||||
{
|
||||
public:
|
||||
sp<MemoryHeapBase> mMemHeap;
|
||||
sp<MemoryBase> mMemBase;
|
||||
audiotrack_callback_cookie mCallbackData{};
|
||||
// TODO do we always want to initialize the callback implementation?
|
||||
AudioTrackJniStorage(jclass audioTrackClass, jobject audioTrackRef, bool isOffload = false)
|
||||
: AudioTrackCallbackImpl(audioTrackClass, audioTrackRef, isOffload) {}
|
||||
|
||||
sp<JNIDeviceCallback> mDeviceCallback;
|
||||
sp<JNIAudioTrackCallback> mAudioTrackCallback;
|
||||
|
||||
bool allocSharedMem(int sizeInBytes) {
|
||||
mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
|
||||
if (mMemHeap->getHeapID() < 0) {
|
||||
return false;
|
||||
}
|
||||
mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
|
||||
return true;
|
||||
jobject getAudioTrackWeakRef() const {
|
||||
return mAudioTrackWeakRef;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class TunerConfigurationHelper {
|
||||
@@ -136,7 +217,6 @@ public:
|
||||
};
|
||||
|
||||
static Mutex sLock;
|
||||
static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
#define DEFAULT_OUTPUT_SAMPLE_RATE 44100
|
||||
@@ -147,102 +227,49 @@ static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
|
||||
#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE (-19)
|
||||
#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED (-20)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
static void audioCallback(int event, void* user, void *info) {
|
||||
|
||||
audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
|
||||
{
|
||||
Mutex::Autolock l(sLock);
|
||||
if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) {
|
||||
return;
|
||||
}
|
||||
callbackInfo->busy = true;
|
||||
}
|
||||
|
||||
// used as default argument when event callback doesn't have any, or number of
|
||||
// frames for EVENT_CAN_WRITE_MORE_DATA
|
||||
int arg = 0;
|
||||
bool postEvent = false;
|
||||
switch (event) {
|
||||
// Offload only events
|
||||
case AudioTrack::EVENT_CAN_WRITE_MORE_DATA:
|
||||
// this event will read the info return parameter of the callback:
|
||||
// for JNI offload, use the returned size to indicate:
|
||||
// 1/ no data is returned through callback, as it's all done through write()
|
||||
// 2/ do not wait as AudioTrack does when it receives 0 bytes
|
||||
if (callbackInfo->isOffload) {
|
||||
AudioTrack::Buffer* pBuffer = (AudioTrack::Buffer*) info;
|
||||
const size_t availableForWrite = pBuffer->size;
|
||||
arg = availableForWrite > INT32_MAX ? INT32_MAX : (int) availableForWrite;
|
||||
pBuffer->size = 0;
|
||||
}
|
||||
FALLTHROUGH_INTENDED;
|
||||
case AudioTrack::EVENT_STREAM_END:
|
||||
case AudioTrack::EVENT_NEW_IAUDIOTRACK: // a.k.a. tear down
|
||||
if (callbackInfo->isOffload) {
|
||||
postEvent = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// PCM and offload events
|
||||
case AudioTrack::EVENT_MARKER:
|
||||
case AudioTrack::EVENT_NEW_POS:
|
||||
postEvent = true;
|
||||
break;
|
||||
default:
|
||||
// event will not be posted
|
||||
break;
|
||||
}
|
||||
|
||||
if (postEvent) {
|
||||
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
||||
if (env != NULL) {
|
||||
env->CallStaticVoidMethod(
|
||||
callbackInfo->audioTrack_class,
|
||||
javaAudioTrackFields.postNativeEventInJava,
|
||||
callbackInfo->audioTrack_ref, event, arg, 0, NULL);
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Mutex::Autolock l(sLock);
|
||||
callbackInfo->busy = false;
|
||||
callbackInfo->cond.broadcast();
|
||||
namespace {
|
||||
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);
|
||||
}
|
||||
// TODO(b/218351957) move somewhere?
|
||||
template<typename T>
|
||||
sp<T> getFieldSp(JNIEnv* env, jobject thiz, jfieldID id)
|
||||
{
|
||||
// make these fields atomic longs on the java side
|
||||
Mutex::Autolock l(sLock);
|
||||
return sp<T>::fromExisting(reinterpret_cast<T*>(env->GetLongField(thiz, id)));
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz)
|
||||
// This (semantically) should only be called on AudioTrack creation and release
|
||||
template <typename T>
|
||||
sp<T> setFieldSp(JNIEnv* env, jobject thiz, const sp<T>& at, jfieldID id)
|
||||
{
|
||||
Mutex::Autolock l(sLock);
|
||||
AudioTrack* const at =
|
||||
(AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
|
||||
return sp<AudioTrack>(at);
|
||||
}
|
||||
|
||||
static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at)
|
||||
{
|
||||
Mutex::Autolock l(sLock);
|
||||
sp<AudioTrack> old =
|
||||
(AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
|
||||
// I don't think this synchronization actually prevents a race
|
||||
// We can still invalidate under our feet in release
|
||||
sp<T> old = sp<T>::fromExisting(reinterpret_cast<T*>(env->GetLongField(thiz, id)));
|
||||
if (at.get()) {
|
||||
at->incStrong((void*)setAudioTrack);
|
||||
at->incStrong((void*)setFieldSp<T>);
|
||||
}
|
||||
if (old != 0) {
|
||||
old->decStrong((void*)setAudioTrack);
|
||||
old->decStrong((void*)setFieldSp<T>);
|
||||
}
|
||||
env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get());
|
||||
env->SetLongField(thiz, id, (jlong)at.get());
|
||||
return old;
|
||||
}
|
||||
|
||||
sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz) {
|
||||
return getFieldSp<AudioTrack>(env, thiz, javaAudioTrackFields.nativeTrackInJavaObj);
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
// ----------------------------------------------------------------------------
|
||||
sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) {
|
||||
return getAudioTrack(env, audioTrackObj);
|
||||
return getFieldSp<AudioTrack>(env, audioTrackObj, javaAudioTrackFields.nativeTrackInJavaObj);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -274,7 +301,6 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
|
||||
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
|
||||
nSession = NULL;
|
||||
|
||||
AudioTrackJniStorage* lpJniStorage = NULL;
|
||||
|
||||
jclass clazz = env->GetObjectClass(thiz);
|
||||
if (clazz == NULL) {
|
||||
@@ -284,6 +310,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
|
||||
|
||||
// if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
|
||||
sp<AudioTrack> lpTrack;
|
||||
const auto lpJniStorage = sp<AudioTrackJniStorage>::make(clazz, weak_this, offload);
|
||||
if (nativeAudioTrack == 0) {
|
||||
if (jaa == 0) {
|
||||
ALOGE("Error creating AudioTrack: invalid audio attributes");
|
||||
@@ -332,7 +359,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
|
||||
AttributionSourceState attributionSource;
|
||||
attributionSource.packageName = std::string(opPackageNameStr.c_str());
|
||||
attributionSource.token = sp<BBinder>::make();
|
||||
lpTrack = new AudioTrack(attributionSource);
|
||||
lpTrack = sp<AudioTrack>::make(attributionSource);
|
||||
|
||||
// read the AudioAttributes values
|
||||
auto paa = JNIAudioAttributeHelper::makeUnique();
|
||||
@@ -345,13 +372,6 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
|
||||
|
||||
// initialize the callback information:
|
||||
// this data will be passed with every AudioTrack callback
|
||||
lpJniStorage = new AudioTrackJniStorage();
|
||||
lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
|
||||
// we use a weak reference so the AudioTrack object can be garbage collected.
|
||||
lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
|
||||
lpJniStorage->mCallbackData.isOffload = offload;
|
||||
lpJniStorage->mCallbackData.busy = false;
|
||||
|
||||
audio_offload_info_t offloadInfo;
|
||||
if (offload == JNI_TRUE) {
|
||||
offloadInfo = AUDIO_INFO_INITIALIZER;
|
||||
@@ -385,8 +405,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
|
||||
nativeChannelMask, offload ? 0 : frameCount,
|
||||
offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD
|
||||
: AUDIO_OUTPUT_FLAG_NONE,
|
||||
audioCallback,
|
||||
&(lpJniStorage->mCallbackData), // callback, callback data (user)
|
||||
lpJniStorage,
|
||||
0, // notificationFrames == 0 since not using EVENT_MORE_DATA
|
||||
// to feed the AudioTrack
|
||||
0, // shared mem
|
||||
@@ -400,9 +419,10 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
|
||||
break;
|
||||
|
||||
case MODE_STATIC:
|
||||
{
|
||||
// AudioTrack is using shared memory
|
||||
|
||||
if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
|
||||
const auto iMem = allocSharedMem(buffSizeInBytes);
|
||||
if (iMem == nullptr) {
|
||||
ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
|
||||
goto native_init_failure;
|
||||
}
|
||||
@@ -412,19 +432,18 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
|
||||
sampleRateInHertz,
|
||||
format, // word length, PCM
|
||||
nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE,
|
||||
audioCallback,
|
||||
&(lpJniStorage->mCallbackData), // callback, callback data (user)
|
||||
lpJniStorage,
|
||||
0, // notificationFrames == 0 since not using EVENT_MORE_DATA
|
||||
// to feed the AudioTrack
|
||||
lpJniStorage->mMemBase, // shared mem
|
||||
iMem, // shared mem
|
||||
true, // thread can call Java
|
||||
sessionId, // audio session ID
|
||||
AudioTrack::TRANSFER_SHARED,
|
||||
NULL, // default offloadInfo
|
||||
nullptr , // default offloadInfo
|
||||
AttributionSourceState(), // default uid, pid values
|
||||
paa.get());
|
||||
break;
|
||||
|
||||
}
|
||||
default:
|
||||
ALOGE("Unknown mode %d", memoryMode);
|
||||
goto native_init_failure;
|
||||
@@ -438,7 +457,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
|
||||
// MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_JAVA
|
||||
lpTrack->setCallerName("java");
|
||||
} else { // end if (nativeAudioTrack == 0)
|
||||
lpTrack = (AudioTrack*)nativeAudioTrack;
|
||||
lpTrack = sp<AudioTrack>::fromExisting(reinterpret_cast<AudioTrack*>(nativeAudioTrack));
|
||||
// TODO: We need to find out which members of the Java AudioTrack might
|
||||
// need to be initialized from the Native AudioTrack
|
||||
// these are directly returned from getters:
|
||||
@@ -456,15 +475,19 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
|
||||
|
||||
// initialize the callback information:
|
||||
// this data will be passed with every AudioTrack callback
|
||||
lpJniStorage = new AudioTrackJniStorage();
|
||||
|
||||
// TODO this callback information is useless, it isn't passed to the
|
||||
// native AudioTrack object
|
||||
/*
|
||||
lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
|
||||
// we use a weak reference so the AudioTrack object can be garbage collected.
|
||||
lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
|
||||
lpJniStorage->mCallbackData.busy = false;
|
||||
*/
|
||||
}
|
||||
lpJniStorage->mAudioTrackCallback =
|
||||
new JNIAudioTrackCallback(env, thiz, lpJniStorage->mCallbackData.audioTrack_ref,
|
||||
javaAudioTrackFields.postNativeEventInJava);
|
||||
sp<JNIAudioTrackCallback>::make(env, thiz, lpJniStorage->getAudioTrackWeakRef(),
|
||||
javaAudioTrackFields.postNativeEventInJava);
|
||||
lpTrack->setAudioTrackCallback(lpJniStorage->mAudioTrackCallback);
|
||||
|
||||
nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
|
||||
@@ -482,17 +505,13 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
|
||||
env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
|
||||
}
|
||||
|
||||
{ // scope for the lock
|
||||
Mutex::Autolock l(sLock);
|
||||
sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
|
||||
}
|
||||
// save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
|
||||
// of the Java object (in mNativeTrackInJavaObj)
|
||||
setAudioTrack(env, thiz, lpTrack);
|
||||
setFieldSp(env, thiz, lpTrack, javaAudioTrackFields.nativeTrackInJavaObj);
|
||||
|
||||
// save the JNI resources so we can free them later
|
||||
//ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);
|
||||
env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);
|
||||
setFieldSp(env, thiz, lpJniStorage, javaAudioTrackFields.jniData);
|
||||
|
||||
// since we had audio attributes, the stream type was derived from them during the
|
||||
// creation of the native AudioTrack: push the same value to the Java object
|
||||
@@ -505,9 +524,6 @@ native_init_failure:
|
||||
if (nSession != NULL) {
|
||||
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
|
||||
}
|
||||
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
|
||||
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
|
||||
delete lpJniStorage;
|
||||
env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
|
||||
|
||||
// lpTrack goes out of scope, so reference count drops to zero
|
||||
@@ -607,38 +623,9 @@ android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, j
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
|
||||
static void android_media_AudioTrack_release(JNIEnv *env, jobject thiz) {
|
||||
sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
|
||||
if (lpTrack == NULL) {
|
||||
return;
|
||||
}
|
||||
//ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
|
||||
|
||||
// delete the JNI data
|
||||
AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
|
||||
thiz, javaAudioTrackFields.jniData);
|
||||
// reset the native resources in the Java object so any attempt to access
|
||||
// them after a call to release fails.
|
||||
env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
|
||||
|
||||
if (pJniStorage) {
|
||||
Mutex::Autolock l(sLock);
|
||||
audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
|
||||
//ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
|
||||
while (lpCookie->busy) {
|
||||
if (lpCookie->cond.waitRelative(sLock,
|
||||
milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
|
||||
NO_ERROR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
sAudioTrackCallBackCookies.remove(lpCookie);
|
||||
// delete global refs created in native_setup
|
||||
env->DeleteGlobalRef(lpCookie->audioTrack_class);
|
||||
env->DeleteGlobalRef(lpCookie->audioTrack_ref);
|
||||
delete pJniStorage;
|
||||
}
|
||||
setFieldSp(env, thiz, sp<AudioTrack>(nullptr), javaAudioTrackFields.nativeTrackInJavaObj);
|
||||
setFieldSp(env, thiz, sp<AudioTrackJniStorage>(nullptr), javaAudioTrackFields.jniData);
|
||||
}
|
||||
|
||||
|
||||
@@ -1249,17 +1236,18 @@ static void android_media_AudioTrack_enableDeviceCallback(
|
||||
JNIEnv *env, jobject thiz) {
|
||||
|
||||
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
|
||||
if (lpTrack == NULL) {
|
||||
if (lpTrack == nullptr) {
|
||||
return;
|
||||
}
|
||||
AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
|
||||
thiz, javaAudioTrackFields.jniData);
|
||||
if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) {
|
||||
const auto pJniStorage =
|
||||
getFieldSp<AudioTrackJniStorage>(env, thiz, javaAudioTrackFields.jniData);
|
||||
if (pJniStorage == nullptr || pJniStorage->mDeviceCallback != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
pJniStorage->mDeviceCallback =
|
||||
new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref,
|
||||
javaAudioTrackFields.postNativeEventInJava);
|
||||
sp<JNIDeviceCallback>::make(env, thiz, pJniStorage->getAudioTrackWeakRef(),
|
||||
javaAudioTrackFields.postNativeEventInJava);
|
||||
lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
|
||||
}
|
||||
|
||||
@@ -1267,12 +1255,13 @@ static void android_media_AudioTrack_disableDeviceCallback(
|
||||
JNIEnv *env, jobject thiz) {
|
||||
|
||||
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
|
||||
if (lpTrack == NULL) {
|
||||
if (lpTrack == nullptr) {
|
||||
return;
|
||||
}
|
||||
AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
|
||||
thiz, javaAudioTrackFields.jniData);
|
||||
if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) {
|
||||
const auto pJniStorage =
|
||||
getFieldSp<AudioTrackJniStorage>(env, thiz, javaAudioTrackFields.jniData);
|
||||
|
||||
if (pJniStorage == nullptr || pJniStorage->mDeviceCallback == nullptr) {
|
||||
return;
|
||||
}
|
||||
lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback);
|
||||
|
||||
@@ -18,15 +18,9 @@
|
||||
#define ANDROID_MEDIA_AUDIOTRACK_H
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
#include <media/AudioTrack.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class AudioTrack;
|
||||
|
||||
}; // namespace android
|
||||
|
||||
/* Gets the underlying AudioTrack from an AudioTrack Java object. */
|
||||
extern android::sp<android::AudioTrack> android_media_AudioTrack_getAudioTrack(
|
||||
JNIEnv* env, jobject audioTrackObj);
|
||||
|
||||
Reference in New Issue
Block a user