Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 449725f9 authored by John Grossman's avatar John Grossman
Browse files

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: default avatarJohn Grossman <johngro@google.com>
parent d8cf2960
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -396,6 +396,9 @@ public class Visualizer {
    public interface OnDataCaptureListener  {
        /**
         * Method called when a new waveform capture is available.
         * <p>Data in the waveform buffer is valid only within the scope of the callback.
         * Applications which needs access to the waveform data after returning from the callback
         * should make a copy of the data instead of holding a reference.
         * @param visualizer Visualizer object on which the listener is registered.
         * @param waveform array of bytes containing the waveform representation.
         * @param samplingRate sampling rate of the audio visualized.
@@ -404,6 +407,9 @@ public class Visualizer {

        /**
         * Method called when a new frequency capture is available.
         * <p>Data in the fft buffer is valid only within the scope of the callback.
         * Applications which needs access to the fft data after returning from the callback
         * should make a copy of the data instead of holding a reference.
         * @param visualizer Visualizer object on which the listener is registered.
         * @param fft array of bytes containing the frequency representation.
         * @param samplingRate sampling rate of the audio visualized.
+80 −6
Original line number Diff line number Diff line
@@ -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