Fix threading issues in AudioRecord JNI

Made native AudioRecord ref based to allow use
of strong pointers in JNI and solve concurrency issues
between read() and release() in particular.

Applied the same fixes to AudioTrack JNI.

Issue 6254582.

Change-Id: I381a66cb00b6639f87f4fcd19a2e202d1a4e6704
This commit is contained in:
Eric Laurent
2012-04-20 12:45:03 -07:00
parent 0b568ff179
commit 532bc1cb7d
2 changed files with 249 additions and 227 deletions

View File

@@ -28,6 +28,8 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
#include <utils/SortedVector.h>
#include <utils/threads.h>
#include <media/AudioRecord.h>
#include <media/mediarecorder.h>
@@ -55,9 +57,12 @@ static fields_t javaAudioRecordFields;
struct audiorecord_callback_cookie {
jclass audioRecord_class;
jobject audioRecord_ref;
};
bool busy;
Condition cond;
};
Mutex sLock;
static Mutex sLock;
static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
// ----------------------------------------------------------------------------
@@ -87,13 +92,21 @@ jint android_media_translateRecorderErrorCode(int code) {
// ----------------------------------------------------------------------------
static void recorderCallback(int event, void* user, void *info) {
audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
{
Mutex::Autolock l(sLock);
if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
return;
}
callbackInfo->busy = true;
}
if (event == AudioRecord::EVENT_MORE_DATA) {
// set size to 0 to signal we're not using the callback to read more data
AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info;
pBuff->size = 0;
} else if (event == AudioRecord::EVENT_MARKER) {
audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user && env) {
env->CallStaticVoidMethod(
@@ -107,7 +120,6 @@ static void recorderCallback(int event, void* user, void *info) {
}
} else if (event == AudioRecord::EVENT_NEW_POS) {
audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user && env) {
env->CallStaticVoidMethod(
@@ -120,8 +132,36 @@ static void recorderCallback(int event, void* user, void *info) {
}
}
}
{
Mutex::Autolock l(sLock);
callbackInfo->busy = false;
callbackInfo->cond.broadcast();
}
}
// ----------------------------------------------------------------------------
static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
AudioRecord* const ar =
(AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
return sp<AudioRecord>(ar);
}
static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
{
Mutex::Autolock l(sLock);
sp<AudioRecord> old =
(AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
if (ar.get()) {
ar->incStrong(thiz);
}
if (old != 0) {
old->decStrong(thiz);
}
env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)ar.get());
return old;
}
// ----------------------------------------------------------------------------
static int
@@ -162,6 +202,12 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
}
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find %s when setting up callback.", kClassPathName);
return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
}
if (jSession == NULL) {
ALOGE("Error creating AudioRecord: invalid session ID pointer");
return AUDIORECORD_ERROR;
@@ -176,27 +222,20 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
audiorecord_callback_cookie *lpCallbackData = NULL;
AudioRecord* lpRecorder = NULL;
// create an uninitialized AudioRecord object
lpRecorder = new AudioRecord();
if (lpRecorder == NULL) {
sp<AudioRecord> lpRecorder = new AudioRecord();
if (lpRecorder == NULL) {
ALOGE("Error creating AudioRecord instance.");
return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
}
// create the callback information:
// this data will be passed with every AudioRecord callback
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find %s when setting up callback.", kClassPathName);
goto native_track_failure;
}
lpCallbackData = new audiorecord_callback_cookie;
audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
// we use a weak reference so the AudioRecord object can be garbage collected.
lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
lpCallbackData->busy = false;
lpRecorder->set((audio_source_t) source,
sampleRateInHertz,
@@ -225,9 +264,13 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
{ // scope for the lock
Mutex::Autolock l(sLock);
sAudioRecordCallBackCookies.add(lpCallbackData);
}
// save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
// of the Java object
env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)lpRecorder);
setAudioRecord(env, thiz, lpRecorder);
// save our newly created callback information in the "nativeCallbackCookie" field
// of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
@@ -240,11 +283,6 @@ native_init_failure:
env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
delete lpCallbackData;
native_track_failure:
delete lpRecorder;
env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
@@ -256,8 +294,7 @@ native_track_failure:
static int
android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
{
AudioRecord *lpRecorder =
(AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
if (lpRecorder == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return AUDIORECORD_ERROR;
@@ -272,8 +309,7 @@ android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint trig
static void
android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
{
AudioRecord *lpRecorder =
(AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
if (lpRecorder == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
@@ -285,30 +321,35 @@ android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
// ----------------------------------------------------------------------------
static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
// serialize access. Ugly, but functional.
Mutex::Autolock lock(&sLock);
AudioRecord *lpRecorder =
(AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
if (lpRecorder == NULL) {
return;
}
ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder.get());
lpRecorder->stop();
audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
thiz, javaAudioRecordFields.nativeCallbackCookie);
// reset the native resources in the Java object so any attempt to access
// them after a call to release fails.
env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
// delete the AudioRecord object
if (lpRecorder) {
ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder);
lpRecorder->stop();
delete lpRecorder;
}
// delete the callback information
if (lpCookie) {
Mutex::Autolock l(sLock);
ALOGV("deleting lpCookie: %x\n", (int)lpCookie);
while (lpCookie->busy) {
if (lpCookie->cond.waitRelative(sLock,
milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
NO_ERROR) {
break;
}
}
sAudioRecordCallBackCookies.remove(lpCookie);
env->DeleteGlobalRef(lpCookie->audioRecord_class);
env->DeleteGlobalRef(lpCookie->audioRecord_ref);
delete lpCookie;
@@ -327,11 +368,8 @@ static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz
jbyteArray javaAudioData,
jint offsetInBytes, jint sizeInBytes) {
jbyte* recordBuff = NULL;
AudioRecord *lpRecorder = NULL;
// get the audio recorder from which we'll read new audio samples
lpRecorder =
(AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
if (lpRecorder == NULL) {
ALOGE("Unable to retrieve AudioRecord object, can't record");
return 0;
@@ -378,12 +416,8 @@ static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thi
// ----------------------------------------------------------------------------
static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
jobject jBuffer, jint sizeInBytes) {
AudioRecord *lpRecorder = NULL;
//ALOGV("Entering android_media_AudioRecord_readInBuffer");
// get the audio recorder from which we'll read new audio samples
lpRecorder =
(AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
if (lpRecorder==NULL)
return 0;
@@ -411,35 +445,29 @@ static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject t
static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
jint markerPos) {
AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
if (lpRecorder) {
return
android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
} else {
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
if (lpRecorder == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioRecord pointer for setMarkerPosition()");
return AUDIORECORD_ERROR;
}
return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
}
// ----------------------------------------------------------------------------
static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
uint32_t markerPos = 0;
if (lpRecorder) {
lpRecorder->getMarkerPosition(&markerPos);
return (jint)markerPos;
} else {
if (lpRecorder == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioRecord pointer for getMarkerPosition()");
return AUDIORECORD_ERROR;
}
lpRecorder->getMarkerPosition(&markerPos);
return (jint)markerPos;
}
@@ -447,35 +475,30 @@ static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz)
static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
jint period) {
AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
if (lpRecorder) {
return
android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
} else {
if (lpRecorder == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
return AUDIORECORD_ERROR;
}
return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
}
// ----------------------------------------------------------------------------
static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
uint32_t period = 0;
if (lpRecorder) {
lpRecorder->getPositionUpdatePeriod(&period);
return (jint)period;
} else {
if (lpRecorder == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
return AUDIORECORD_ERROR;
}
lpRecorder->getPositionUpdatePeriod(&period);
return (jint)period;
}
@@ -486,7 +509,8 @@ static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobjec
static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat);
ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
sampleRateInHertz, nbChannels, audioFormat);
int frameCount = 0;
status_t result = AudioRecord::getMinFrameCount(&frameCount,

View File

@@ -59,7 +59,9 @@ static fields_t javaAudioTrackFields;
struct audiotrack_callback_cookie {
jclass audioTrack_class;
jobject audioTrack_ref;
};
bool busy;
Condition cond;
};
// ----------------------------------------------------------------------------
class AudioTrackJniStorage {
@@ -90,6 +92,9 @@ class AudioTrackJniStorage {
}
};
static Mutex sLock;
static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
// ----------------------------------------------------------------------------
#define DEFAULT_OUTPUT_SAMPLE_RATE 44100
@@ -120,13 +125,22 @@ jint android_media_translateErrorCode(int code) {
// ----------------------------------------------------------------------------
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;
}
if (event == AudioTrack::EVENT_MORE_DATA) {
// set size to 0 to signal we're not using the callback to write more data
AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info;
pBuff->size = 0;
} else if (event == AudioTrack::EVENT_MARKER) {
audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user && env) {
env->CallStaticVoidMethod(
@@ -140,7 +154,6 @@ static void audioCallback(int event, void* user, void *info) {
}
} else if (event == AudioTrack::EVENT_NEW_POS) {
audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user && env) {
env->CallStaticVoidMethod(
@@ -153,9 +166,38 @@ static void audioCallback(int event, void* user, void *info) {
}
}
}
{
Mutex::Autolock l(sLock);
callbackInfo->busy = false;
callbackInfo->cond.broadcast();
}
}
// ----------------------------------------------------------------------------
static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
AudioTrack* const at =
(AudioTrack*)env->GetIntField(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->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (at.get()) {
at->incStrong(thiz);
}
if (old != 0) {
old->decStrong(thiz);
}
env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)at.get());
return old;
}
// ----------------------------------------------------------------------------
static int
android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
@@ -231,32 +273,20 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
// initialize the callback information:
// this data will be passed with every AudioTrack callback
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find %s when setting up callback.", kClassPathName);
delete lpJniStorage;
return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}
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->mStreamType = atStreamType;
if (jSession == NULL) {
ALOGE("Error creating AudioTrack: invalid session ID pointer");
delete lpJniStorage;
return AUDIOTRACK_ERROR;
}
jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
if (nSession == NULL) {
ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
delete lpJniStorage;
return AUDIOTRACK_ERROR;
}
int sessionId = nSession[0];
@@ -264,12 +294,21 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
nSession = NULL;
// create the native AudioTrack object
AudioTrack* lpTrack = new AudioTrack();
sp<AudioTrack> lpTrack = new AudioTrack();
if (lpTrack == NULL) {
ALOGE("Error creating uninitialized AudioTrack");
goto native_track_failure;
return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}
// initialize the callback information:
// this data will be passed with every AudioTrack callback
AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
lpJniStorage->mStreamType = atStreamType;
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;
// initialize the native AudioTrack object
if (memoryMode == javaAudioTrackFields.MODE_STREAM) {
@@ -323,9 +362,13 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
{ // 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)
env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);
setAudioTrack(env, thiz, lpTrack);
// save the JNI resources so we can free them later
//ALOGV("storing lpJniStorage: %x\n", (int)lpJniStorage);
@@ -335,10 +378,6 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
// failures:
native_init_failure:
delete lpTrack;
env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
native_track_failure:
if (nSession != NULL) {
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
}
@@ -346,8 +385,8 @@ native_track_failure:
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
delete lpJniStorage;
env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}
@@ -355,9 +394,8 @@ native_track_failure:
static void
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
{
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack == NULL ) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for start()");
return;
@@ -371,9 +409,8 @@ android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
static void
android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
{
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack == NULL ) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for stop()");
return;
@@ -387,9 +424,8 @@ android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
static void
android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
{
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack == NULL ) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for pause()");
return;
@@ -403,9 +439,8 @@ android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
static void
android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
{
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack == NULL ) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for flush()");
return;
@@ -418,9 +453,8 @@ android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
static void
android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
{
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack == NULL ) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for setVolume()");
return;
@@ -430,68 +464,75 @@ android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, j
}
// ----------------------------------------------------------------------------
static void android_media_AudioTrack_native_finalize(JNIEnv *env, jobject thiz) {
//ALOGV("android_media_AudioTrack_native_finalize jobject: %x\n", (int)thiz);
// delete the AudioTrack object
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
//ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
lpTrack->stop();
delete lpTrack;
#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
static void android_media_AudioTrack_native_release(JNIEnv *env, jobject thiz) {
sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
if (lpTrack == NULL) {
return;
}
//ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
lpTrack->stop();
// delete the JNI data
AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetIntField(
thiz, javaAudioTrackFields.jniData);
// reset the native resources in the Java object so any attempt to access
// them after a call to release fails.
env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
if (pJniStorage) {
// delete global refs created in native_setup
env->DeleteGlobalRef(pJniStorage->mCallbackData.audioTrack_class);
env->DeleteGlobalRef(pJniStorage->mCallbackData.audioTrack_ref);
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;
}
}
// ----------------------------------------------------------------------------
static void android_media_AudioTrack_native_release(JNIEnv *env, jobject thiz) {
// do everything a call to finalize would
android_media_AudioTrack_native_finalize(env, thiz);
// + reset the native resources in the Java object so any attempt to access
// them after a call to release fails.
env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
// ----------------------------------------------------------------------------
static void android_media_AudioTrack_native_finalize(JNIEnv *env, jobject thiz) {
//ALOGV("android_media_AudioTrack_native_finalize jobject: %x\n", (int)thiz);
android_media_AudioTrack_native_release(env, thiz);
}
// ----------------------------------------------------------------------------
jint writeToTrack(AudioTrack* pTrack, jint audioFormat, jbyte* data,
jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data,
jint offsetInBytes, jint sizeInBytes) {
// give the data to the native AudioTrack object (the data starts at the offset)
ssize_t written = 0;
// regular write() or copy the data to the AudioTrack's shared memory?
if (pTrack->sharedBuffer() == 0) {
written = pTrack->write(data + offsetInBytes, sizeInBytes);
if (track->sharedBuffer() == 0) {
written = track->write(data + offsetInBytes, sizeInBytes);
} else {
if (audioFormat == javaAudioTrackFields.PCM16) {
// writing to shared memory, check for capacity
if ((size_t)sizeInBytes > pTrack->sharedBuffer()->size()) {
sizeInBytes = pTrack->sharedBuffer()->size();
if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
sizeInBytes = track->sharedBuffer()->size();
}
memcpy(pTrack->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
written = sizeInBytes;
} else if (audioFormat == javaAudioTrackFields.PCM8) {
// data contains 8bit data we need to expand to 16bit before copying
// to the shared memory
// writing to shared memory, check for capacity,
// note that input data will occupy 2X the input space due to 8 to 16bit conversion
if (((size_t)sizeInBytes)*2 > pTrack->sharedBuffer()->size()) {
sizeInBytes = pTrack->sharedBuffer()->size() / 2;
if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) {
sizeInBytes = track->sharedBuffer()->size() / 2;
}
int count = sizeInBytes;
int16_t *dst = (int16_t *)pTrack->sharedBuffer()->pointer();
int16_t *dst = (int16_t *)track->sharedBuffer()->pointer();
const int8_t *src = (const int8_t *)(data + offsetInBytes);
while (count--) {
*dst++ = (int16_t)(*src++^0x80) << 8;
@@ -510,13 +551,9 @@ static jint android_media_AudioTrack_native_write_byte(JNIEnv *env, jobject thi
jbyteArray javaAudioData,
jint offsetInBytes, jint sizeInBytes,
jint javaAudioFormat) {
jbyte* cAudioData = NULL;
AudioTrack *lpTrack = NULL;
//ALOGV("android_media_AudioTrack_native_write_byte(offset=%d, sizeInBytes=%d) called",
// offsetInBytes, sizeInBytes);
// get the audio track to load with samples
lpTrack = (AudioTrack *)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for write()");
@@ -528,6 +565,7 @@ static jint android_media_AudioTrack_native_write_byte(JNIEnv *env, jobject thi
// a way that it becomes much more efficient. When doing so, we will have to prevent the
// AudioSystem callback to be called while in critical section (in case of media server
// process crash for instance)
jbyte* cAudioData = NULL;
if (javaAudioData) {
cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
if (cAudioData == NULL) {
@@ -564,183 +602,148 @@ static jint android_media_AudioTrack_native_write_short(JNIEnv *env, jobject th
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobject thiz) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
return lpTrack->frameCount();
} else {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for frameCount()");
return AUDIOTRACK_ERROR;
}
return lpTrack->frameCount();
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env, jobject thiz,
jint sampleRateInHz) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
return android_media_translateErrorCode(lpTrack->setSampleRate(sampleRateInHz));
} else {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for setSampleRate()");
return AUDIOTRACK_ERROR;
}
return android_media_translateErrorCode(lpTrack->setSampleRate(sampleRateInHz));
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env, jobject thiz) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
return (jint) lpTrack->getSampleRate();
} else {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for getSampleRate()");
return AUDIOTRACK_ERROR;
}
return (jint) lpTrack->getSampleRate();
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env, jobject thiz,
jint markerPos) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
return android_media_translateErrorCode( lpTrack->setMarkerPosition(markerPos) );
} else {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for setMarkerPosition()");
return AUDIOTRACK_ERROR;
}
return android_media_translateErrorCode( lpTrack->setMarkerPosition(markerPos) );
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env, jobject thiz) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
uint32_t markerPos = 0;
if (lpTrack) {
lpTrack->getMarkerPosition(&markerPos);
return (jint)markerPos;
} else {
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for getMarkerPosition()");
return AUDIOTRACK_ERROR;
}
lpTrack->getMarkerPosition(&markerPos);
return (jint)markerPos;
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env, jobject thiz,
jint period) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
return android_media_translateErrorCode( lpTrack->setPositionUpdatePeriod(period) );
} else {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
return AUDIOTRACK_ERROR;
}
return android_media_translateErrorCode( lpTrack->setPositionUpdatePeriod(period) );
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env, jobject thiz) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
uint32_t period = 0;
if (lpTrack) {
lpTrack->getPositionUpdatePeriod(&period);
return (jint)period;
} else {
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
return AUDIOTRACK_ERROR;
}
lpTrack->getPositionUpdatePeriod(&period);
return (jint)period;
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_set_position(JNIEnv *env, jobject thiz,
jint position) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
return android_media_translateErrorCode( lpTrack->setPosition(position) );
} else {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for setPosition()");
return AUDIOTRACK_ERROR;
}
return android_media_translateErrorCode( lpTrack->setPosition(position) );
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_get_position(JNIEnv *env, jobject thiz) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
uint32_t position = 0;
if (lpTrack) {
lpTrack->getPosition(&position);
return (jint)position;
} else {
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for getPosition()");
return AUDIOTRACK_ERROR;
}
lpTrack->getPosition(&position);
return (jint)position;
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_set_loop(JNIEnv *env, jobject thiz,
jint loopStart, jint loopEnd, jint loopCount) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
return android_media_translateErrorCode( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
} else {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for setLoop()");
return AUDIOTRACK_ERROR;
}
return android_media_translateErrorCode( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
}
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_reload(JNIEnv *env, jobject thiz) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
return android_media_translateErrorCode( lpTrack->reload() );
} else {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for reload()");
return AUDIOTRACK_ERROR;
}
return android_media_translateErrorCode( lpTrack->reload() );
}
@@ -795,8 +798,7 @@ static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thi
static void
android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
{
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
@@ -809,17 +811,13 @@ android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env, jobject thiz,
jint effectId) {
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack) {
return android_media_translateErrorCode( lpTrack->attachAuxEffect(effectId) );
} else {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for attachAuxEffect()");
return AUDIOTRACK_ERROR;
}
return android_media_translateErrorCode( lpTrack->attachAuxEffect(effectId) );
}
// ----------------------------------------------------------------------------