diff options
author | John Grossman <johngro@google.com> | 2012-01-10 12:17:03 -0800 |
---|---|---|
committer | John Grossman <johngro@google.com> | 2012-02-16 13:45:11 -0800 |
commit | 449725f9aa67136a38c7554ba76ac4e27e5e3bd3 (patch) | |
tree | f39c96b0e9520e8fa850393388a51ac54c384a55 /media/jni | |
parent | d8cf2960d0828121d67ad0234c648f193a90c86a (diff) | |
download | frameworks_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.cpp | 86 |
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 |