summaryrefslogtreecommitdiffstats
path: root/media/jni
diff options
context:
space:
mode:
authorJohn Grossman <johngro@google.com>2012-01-10 12:17:03 -0800
committerJohn Grossman <johngro@google.com>2012-02-16 13:45:11 -0800
commit449725f9aa67136a38c7554ba76ac4e27e5e3bd3 (patch)
treef39c96b0e9520e8fa850393388a51ac54c384a55 /media/jni
parentd8cf2960d0828121d67ad0234c648f193a90c86a (diff)
downloadframeworks_base-449725f9aa67136a38c7554ba76ac4e27e5e3bd3.zip
frameworks_base-449725f9aa67136a38c7554ba76ac4e27e5e3bd3.tar.gz
frameworks_base-449725f9aa67136a38c7554ba76ac4e27e5e3bd3.tar.bz2
Reuse callback buffers in the Visualizer.
Don't re-allocate buffers used by Visualizer callbacks as this causes an unacceptable amount of GC thrash. Instead, lazily allocate the buffers and only reallocate them when the required size changes. See http://b/issue?id=5717519 for details. Change-Id: Ibd157ed51f30687ce7c4ef0b4003258a484e0f5d Signed-off-by: John Grossman <johngro@google.com>
Diffstat (limited to 'media/jni')
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp86
1 files changed, 80 insertions, 6 deletions
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index ecd4d07..433c459 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -23,6 +23,7 @@
#include <nativehelper/jni.h>
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
+#include <utils/threads.h>
#include "media/Visualizer.h"
using namespace android;
@@ -54,6 +55,43 @@ static fields_t fields;
struct visualizer_callback_cookie {
jclass visualizer_class; // Visualizer class
jobject visualizer_ref; // Visualizer object instance
+
+ // Lazily allocated arrays used to hold callback data provided to java
+ // applications. These arrays are allocated during the first callback and
+ // reallocated when the size of the callback data changes. Allocating on
+ // demand and saving the arrays means that applications cannot safely hold a
+ // reference to the provided data (they need to make a copy if they want to
+ // hold onto outside of the callback scope), but it avoids GC thrash caused
+ // by constantly allocating and releasing arrays to hold callback data.
+ Mutex callback_data_lock;
+ jbyteArray waveform_data;
+ jbyteArray fft_data;
+
+ visualizer_callback_cookie() {
+ waveform_data = NULL;
+ fft_data = NULL;
+ }
+
+ ~visualizer_callback_cookie() {
+ cleanupBuffers();
+ }
+
+ void cleanupBuffers() {
+ AutoMutex lock(&callback_data_lock);
+ if (waveform_data || fft_data) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ if (waveform_data) {
+ env->DeleteGlobalRef(waveform_data);
+ waveform_data = NULL;
+ }
+
+ if (fft_data) {
+ env->DeleteGlobalRef(fft_data);
+ fft_data = NULL;
+ }
+ }
+ }
};
// ----------------------------------------------------------------------------
@@ -66,7 +104,6 @@ class visualizerJniStorage {
~visualizerJniStorage() {
}
-
};
@@ -93,6 +130,26 @@ static jint translateError(int code) {
// ----------------------------------------------------------------------------
+static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
+ if (NULL != *array) {
+ uint32_t len = env->GetArrayLength(*array);
+ if (len == size)
+ return;
+
+ env->DeleteGlobalRef(*array);
+ *array = NULL;
+ }
+
+ jbyteArray localRef = env->NewByteArray(size);
+ if (NULL != localRef) {
+ // Promote to global ref.
+ *array = (jbyteArray)env->NewGlobalRef(localRef);
+
+ // Release our (now pointless) local ref.
+ env->DeleteLocalRef(localRef);
+ }
+}
+
static void captureCallback(void* user,
uint32_t waveformSize,
uint8_t *waveform,
@@ -106,6 +163,7 @@ static void captureCallback(void* user,
visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
+ AutoMutex lock(&callbackInfo->callback_data_lock);
ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
callbackInfo,
@@ -118,7 +176,11 @@ static void captureCallback(void* user,
}
if (waveformSize != 0 && waveform != NULL) {
- jbyteArray jArray = env->NewByteArray(waveformSize);
+ jbyteArray jArray;
+
+ ensureArraySize(env, &callbackInfo->waveform_data, waveformSize);
+ jArray = callbackInfo->waveform_data;
+
if (jArray != NULL) {
jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
memcpy(nArray, waveform, waveformSize);
@@ -131,12 +193,15 @@ static void captureCallback(void* user,
samplingrate,
0,
jArray);
- env->DeleteLocalRef(jArray);
}
}
if (fftSize != 0 && fft != NULL) {
- jbyteArray jArray = env->NewByteArray(fftSize);
+ jbyteArray jArray;
+
+ ensureArraySize(env, &callbackInfo->fft_data, fftSize);
+ jArray = callbackInfo->fft_data;
+
if (jArray != NULL) {
jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
memcpy(nArray, fft, fftSize);
@@ -149,7 +214,6 @@ static void captureCallback(void* user,
samplingrate,
0,
jArray);
- env->DeleteLocalRef(jArray);
}
}
@@ -345,7 +409,17 @@ android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean e
return VISUALIZER_ERROR_NO_INIT;
}
- return translateError(lpVisualizer->setEnabled(enabled));
+ jint retVal = translateError(lpVisualizer->setEnabled(enabled));
+
+ if (!enabled) {
+ visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(
+ thiz, fields.fidJniData);
+
+ if (NULL != lpJniStorage)
+ lpJniStorage->mCallbackData.cleanupBuffers();
+ }
+
+ return retVal;
}
static jboolean