From df94ad0c4868f4f31cff2d7bbdb153666c88cbe2 Mon Sep 17 00:00:00 2001 From: Vincent Pizzo Date: Thu, 11 Apr 2013 15:29:35 -0500 Subject: [PATCH] -Adding small performance tweaks --- build_jni.sh | 0 .../org_xiph_vorbis_decoder_VorbisDecoder.c | 72 ++++++++++-------- .../org_xiph_vorbis_decoder_VorbisDecoder.h | 4 +- .../org_xiph_vorbis_encoder_VorbisEncoder.c | 45 ++++++----- .../org_xiph_vorbis_encoder_VorbisEncoder.h | 4 +- libs/armeabi/libvorbis-jni.so | Bin 25724 -> 25724 bytes 6 files changed, 68 insertions(+), 57 deletions(-) mode change 100644 => 100755 build_jni.sh diff --git a/build_jni.sh b/build_jni.sh old mode 100644 new mode 100755 diff --git a/jni/libvorbis-jni/org_xiph_vorbis_decoder_VorbisDecoder.c b/jni/libvorbis-jni/org_xiph_vorbis_decoder_VorbisDecoder.c index c701f18..e98f918 100644 --- a/jni/libvorbis-jni/org_xiph_vorbis_decoder_VorbisDecoder.c +++ b/jni/libvorbis-jni/org_xiph_vorbis_decoder_VorbisDecoder.c @@ -13,6 +13,8 @@ to end. */ #define PREMATURE_END_OF_FILE -26 #define SUCCESS 0 +#define BUFFER_LENGTH 4096 + extern void _VDBG_dump(void); //Stops the vorbis data feed @@ -40,48 +42,39 @@ void throwDecodeException(JNIEnv *env, const int code, jobject* vorbisDataFeed, } //Reads raw vorbis data from the jni callback -int readVorbisDataFromVorbisDataFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* readVorbisDataMethodId, char* buffer, int length) { +int readVorbisDataFromVorbisDataFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* readVorbisDataMethodId, char* buffer, jbyteArray* jByteArrayReadBuffer) { + //Call the read method + int readByteCount = (*env)->CallIntMethod(env, (*vorbisDataFeed), (*readVorbisDataMethodId), (*jByteArrayReadBuffer), BUFFER_LENGTH); - //create a new java byte array to pass to the vorbis data feed method - jbyteArray jByteArray = (*env)->NewByteArray(env, length); - int readByteCount = (*env)->CallIntMethod(env, (*vorbisDataFeed), (*readVorbisDataMethodId), jByteArray, length); - - //Don't bother copying, just delete the reference and return 0 + //Don't bother copying, just return 0 if(readByteCount == 0) { - (*env)->DeleteLocalRef(env, jByteArray); return 0; } - - + //Gets the bytes from the java array and copies them to the vorbis buffer - jbyte* readBytes = (*env)->GetByteArrayElements(env, jByteArray, NULL); + jbyte* readBytes = (*env)->GetByteArrayElements(env, (*jByteArrayReadBuffer), NULL); memcpy(buffer, readBytes, readByteCount); //Clean up memory and return how much data was read - (*env)->ReleaseByteArrayElements(env, jByteArray, readBytes, JNI_ABORT); - (*env)->DeleteLocalRef(env, jByteArray); + (*env)->ReleaseByteArrayElements(env, (*jByteArrayReadBuffer), readBytes, JNI_ABORT); + + //Return the amount actually read return readByteCount; - } //Writes the pcm data to the Java layer -void writePCMDataFromVorbisDataFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* writePCMDataMethodId, ogg_int16_t* buffer, int bytes) { +void writePCMDataFromVorbisDataFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* writePCMDataMethodId, ogg_int16_t* buffer, int bytes, jshortArray* jShortArrayWriteBuffer) { //No data to read, just exit if(bytes == 0) { return; } - //Create and copy the contents of what we're writing to the java short array - jshortArray jShortArray = (*env)->NewShortArray(env, bytes); - (*env)->SetShortArrayRegion(env, jShortArray, 0, bytes, (jshort *)buffer); + //Copy the contents of what we're writing to the java short array + (*env)->SetShortArrayRegion(env, (*jShortArrayWriteBuffer), 0, bytes, (jshort *)buffer); //Call the write pcm data method - (*env)->CallVoidMethod(env, (*vorbisDataFeed), (*writePCMDataMethodId), jShortArray, bytes); - - //cleanup - (*env)->DeleteLocalRef(env, jShortArray); - + (*env)->CallVoidMethod(env, (*vorbisDataFeed), (*writePCMDataMethodId), (*jShortArrayWriteBuffer), bytes); } //Starts the decode feed with the necessary information about sample rates, channels, etc about the stream @@ -119,6 +112,12 @@ void startReadingHeader(JNIEnv *env, jobject *vorbisDataFeed, jmethodID* startRe JNIEXPORT int JNICALL Java_org_xiph_vorbis_decoder_VorbisDecoder_startDecoding (JNIEnv *env, jclass cls, jobject vorbisDataFeed) { + //Create a new java byte array to pass to the vorbis data feed method + jbyteArray jByteArrayReadBuffer = (*env)->NewByteArray(env, BUFFER_LENGTH); + + //Create our write buffer + jshortArray jShortArrayWriteBuffer = (*env)->NewShortArray(env, BUFFER_LENGTH*2); + //Find our java classes we'll be calling jclass vorbisDataFeedClass = (*env)->FindClass(env, "org/xiph/vorbis/decoder/DecodeFeed"); @@ -129,8 +128,8 @@ JNIEXPORT int JNICALL Java_org_xiph_vorbis_decoder_VorbisDecoder_startDecoding jmethodID startReadingHeaderMethodId = (*env)->GetMethodID(env, vorbisDataFeedClass, "startReadingHeader", "()V"); jmethodID stopMethodId = (*env)->GetMethodID(env, vorbisDataFeedClass, "stop", "()V"); - ogg_int16_t convbuffer[4096]; /* take 8k out of the data segment, not the stack */ - int convsize=4096; + ogg_int16_t convbuffer[BUFFER_LENGTH]; /* take 8k out of the data segment, not the stack */ + int convsize=BUFFER_LENGTH; ogg_sync_state oy; /* sync and verify incoming physical bitstream */ ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */ @@ -164,15 +163,15 @@ JNIEXPORT int JNICALL Java_org_xiph_vorbis_decoder_VorbisDecoder_startDecoding /* submit a 4k block to libvorbis' Ogg layer */ __android_log_print(ANDROID_LOG_INFO, "VorbisDecoder", "Submitting 4k block to libvorbis' Ogg layer"); - buffer=ogg_sync_buffer(&oy,4096); - bytes=readVorbisDataFromVorbisDataFeed(env, &vorbisDataFeed, &readVorbisDataMethodId, buffer, 4096); + buffer=ogg_sync_buffer(&oy,BUFFER_LENGTH); + bytes=readVorbisDataFromVorbisDataFeed(env, &vorbisDataFeed, &readVorbisDataMethodId, buffer, &jByteArrayReadBuffer); ogg_sync_wrote(&oy,bytes); /* Get the first page. */ __android_log_print(ANDROID_LOG_DEBUG, "VorbisDecoder", "Getting the first page, read (%d) bytes", bytes); if(ogg_sync_pageout(&oy,&og)!=1){ /* have we simply run out of data? If so, we're done. */ - if(bytes<4096)break; + if(bytesPCM decoder. */ @@ -365,7 +364,7 @@ JNIEXPORT int JNICALL Java_org_xiph_vorbis_decoder_VorbisDecoder_startDecoding __android_log_print(ANDROID_LOG_INFO, "VorbisDecoder", "Clipping in frame %ld\n",(long)(vd.sequence)); } - writePCMDataFromVorbisDataFeed(env, &vorbisDataFeed, &writePCMDataMethodId, &convbuffer[0], bout*vi.channels); + writePCMDataFromVorbisDataFeed(env, &vorbisDataFeed, &writePCMDataMethodId, &convbuffer[0], bout*vi.channels, &jShortArrayWriteBuffer); vorbis_synthesis_read(&vd,bout); /* tell libvorbis how many samples we actually consumed */ } @@ -376,8 +375,8 @@ JNIEXPORT int JNICALL Java_org_xiph_vorbis_decoder_VorbisDecoder_startDecoding } if(!eos){ - buffer=ogg_sync_buffer(&oy,4096); - bytes=readVorbisDataFromVorbisDataFeed(env, &vorbisDataFeed, &readVorbisDataMethodId, buffer, 4096); + buffer=ogg_sync_buffer(&oy,BUFFER_LENGTH); + bytes=readVorbisDataFromVorbisDataFeed(env, &vorbisDataFeed, &readVorbisDataMethodId, buffer, &jByteArrayReadBuffer); ogg_sync_wrote(&oy,bytes); if(bytes==0) { eos=1; @@ -406,6 +405,13 @@ JNIEXPORT int JNICALL Java_org_xiph_vorbis_decoder_VorbisDecoder_startDecoding /* OK, clean up the framer */ ogg_sync_clear(&oy); + + stopDecodeFeed(env, &vorbisDataFeed, &stopMethodId); + + //Clean up our buffers + (*env)->DeleteLocalRef(env, jByteArrayReadBuffer); + (*env)->DeleteLocalRef(env, jShortArrayWriteBuffer); + return SUCCESS; } \ No newline at end of file diff --git a/jni/libvorbis-jni/org_xiph_vorbis_decoder_VorbisDecoder.h b/jni/libvorbis-jni/org_xiph_vorbis_decoder_VorbisDecoder.h index cb24d86..ca07f1c 100644 --- a/jni/libvorbis-jni/org_xiph_vorbis_decoder_VorbisDecoder.h +++ b/jni/libvorbis-jni/org_xiph_vorbis_decoder_VorbisDecoder.h @@ -23,10 +23,10 @@ void stopDecodeFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* stopMethodI void throwDecodeException(JNIEnv *env, const int code, jobject* vorbisDataFeed, jmethodID* stopMethodId); //Reads raw vorbis data from the jni callback -int readVorbisDataFromVorbisDataFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* readVorbisDataMethodId, char* buffer, int length); +int readVorbisDataFromVorbisDataFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* readVorbisDataMethodId, char* buffer, jbyteArray* jByteArrayReadBuffer); //Writes the pcm data to the Java layer -void writePCMDataFromVorbisDataFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* writePCMDataMethodId, ogg_int16_t* buffer, int bytes); +void writePCMDataFromVorbisDataFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* writePCMDataMethodId, ogg_int16_t* buffer, int bytes, jshortArray* jShortArrayWriteBuffer); //Starts the decode feed with the necessary information about sample rates, channels, etc about the stream void start(JNIEnv *env, jobject *vorbisDataFeed, jmethodID* startMethodId, long sampleRate, long channels, char* vendor); diff --git a/jni/libvorbis-jni/org_xiph_vorbis_encoder_VorbisEncoder.c b/jni/libvorbis-jni/org_xiph_vorbis_encoder_VorbisEncoder.c index 4012eac..ab0cc42 100644 --- a/jni/libvorbis-jni/org_xiph_vorbis_encoder_VorbisEncoder.c +++ b/jni/libvorbis-jni/org_xiph_vorbis_encoder_VorbisEncoder.c @@ -40,31 +40,27 @@ void stopEncodeFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* stopMethodI } //Reads pcm data from the jni callback -long readPCMDataFromEncoderDataFeed(JNIEnv *env, jobject* encoderDataFeed, jmethodID* readPCMDataMethodId, char* buffer, int length) { - - //create a new java byte array to pass to the data feed method - jbyteArray jByteArray = (*env)->NewByteArray(env, length); - long readByteCount = (*env)->CallLongMethod(env, (*encoderDataFeed), (*readPCMDataMethodId), jByteArray, length); +long readPCMDataFromEncoderDataFeed(JNIEnv *env, jobject* encoderDataFeed, jmethodID* readPCMDataMethodId, char* buffer, int length, jbyteArray* jByteArrayBuffer) { + long readByteCount = (*env)->CallLongMethod(env, (*encoderDataFeed), (*readPCMDataMethodId), (*jByteArrayBuffer), length); //Don't bother copying, just delete the reference and return 0 if(readByteCount == 0) { - (*env)->DeleteLocalRef(env, jByteArray); + (*env)->DeleteLocalRef(env, (*jByteArrayBuffer)); return 0; } //Gets the bytes from the java array and copies them to the pcm buffer - jbyte* readBytes = (*env)->GetByteArrayElements(env, jByteArray, NULL); + jbyte* readBytes = (*env)->GetByteArrayElements(env, (*jByteArrayBuffer), NULL); memcpy(buffer, readBytes, readByteCount); //Clean up memory and return how much data was read - (*env)->ReleaseByteArrayElements(env, jByteArray, readBytes, JNI_ABORT); - (*env)->DeleteLocalRef(env, jByteArray); + (*env)->ReleaseByteArrayElements(env, (*jByteArrayBuffer), readBytes, JNI_ABORT); return readByteCount; } //Writes the vorbis data to the Java layer -int writeVorbisDataToEncoderDataFeed(JNIEnv *env, jobject* encoderDataFeed, jmethodID* writeVorbisDataMethodId, char* buffer, int bytes) { +int writeVorbisDataToEncoderDataFeed(JNIEnv *env, jobject* encoderDataFeed, jmethodID* writeVorbisDataMethodId, char* buffer, int bytes, jbyteArray* jByteArrayWriteBuffer) { //No data to write, just exit if(bytes == 0) { @@ -73,14 +69,12 @@ int writeVorbisDataToEncoderDataFeed(JNIEnv *env, jobject* encoderDataFeed, jmet //Create and copy the contents of what we're writing to the java byte array jbyteArray jByteArray = (*env)->NewByteArray(env, bytes); - (*env)->SetByteArrayRegion(env, jByteArray, 0, bytes, (jbyte *)buffer); + (*env)->SetByteArrayRegion(env, (*jByteArrayWriteBuffer), 0, bytes, (jbyte *)buffer); //Call the write vorbis data method - int amountWritten = (*env)->CallIntMethod(env, (*encoderDataFeed), (*writeVorbisDataMethodId), jByteArray, bytes); - - //cleanup - (*env)->DeleteLocalRef(env, jByteArray); + int amountWritten = (*env)->CallIntMethod(env, (*encoderDataFeed), (*writeVorbisDataMethodId), (*jByteArrayWriteBuffer), bytes); + //Return the amount that was actually written return amountWritten; } @@ -90,6 +84,12 @@ JNIEXPORT int JNICALL Java_org_xiph_vorbis_encoder_VorbisEncoder_startEncoding //Create our PCM data buffer signed char readbuffer[READ*4+44]; + //Create a new java byte array to pass to the data feed method + jbyteArray jByteArrayBuffer = (*env)->NewByteArray(env, READ*4); + + //Create a new java byte buffer to write to + jbyteArray jByteArrayWriteBuffer = (*env)->NewByteArray(env, READ*8); + //Find our java classes we'll be calling jclass encoderDataFeedClass = (*env)->FindClass(env, "org/xiph/vorbis/encoder/EncodeFeed"); @@ -201,8 +201,8 @@ JNIEXPORT int JNICALL Java_org_xiph_vorbis_encoder_VorbisEncoder_startEncoding while(!eos){ int result=ogg_stream_flush(&os,&og); if(result==0)break; - writeVorbisDataToEncoderDataFeed(env, &encoderDataFeed, &writeVorbisDataMethodId, og.header, og.header_len); - writeVorbisDataToEncoderDataFeed(env, &encoderDataFeed, &writeVorbisDataMethodId, og.body, og.body_len); + writeVorbisDataToEncoderDataFeed(env, &encoderDataFeed, &writeVorbisDataMethodId, og.header, og.header_len, &jByteArrayWriteBuffer); + writeVorbisDataToEncoderDataFeed(env, &encoderDataFeed, &writeVorbisDataMethodId, og.body, og.body_len, &jByteArrayWriteBuffer); } } @@ -210,7 +210,7 @@ JNIEXPORT int JNICALL Java_org_xiph_vorbis_encoder_VorbisEncoder_startEncoding __android_log_print(ANDROID_LOG_INFO, "VorbisEncoder", "Starting to read from pcm callback"); while(!eos){ long i; - long bytes = readPCMDataFromEncoderDataFeed(env, &encoderDataFeed, &readPCMDataMethodId, readbuffer, READ*4); + long bytes = readPCMDataFromEncoderDataFeed(env, &encoderDataFeed, &readPCMDataMethodId, readbuffer, READ*4, &jByteArrayBuffer); if(bytes==0){ /* end of file. this can be done implicitly in the mainline, @@ -257,8 +257,8 @@ JNIEXPORT int JNICALL Java_org_xiph_vorbis_encoder_VorbisEncoder_startEncoding while(!eos){ int result=ogg_stream_pageout(&os,&og); if(result==0)break; - writeVorbisDataToEncoderDataFeed(env, &encoderDataFeed, &writeVorbisDataMethodId, og.header, og.header_len); - writeVorbisDataToEncoderDataFeed(env, &encoderDataFeed, &writeVorbisDataMethodId, og.body, og.body_len); + writeVorbisDataToEncoderDataFeed(env, &encoderDataFeed, &writeVorbisDataMethodId, og.header, og.header_len, &jByteArrayWriteBuffer); + writeVorbisDataToEncoderDataFeed(env, &encoderDataFeed, &writeVorbisDataMethodId, og.body, og.body_len, &jByteArrayWriteBuffer); /* this could be set above, but for illustrative purposes, I do it here (to show that vorbis does know where the stream ends) */ @@ -281,5 +281,10 @@ JNIEXPORT int JNICALL Java_org_xiph_vorbis_encoder_VorbisEncoder_startEncoding libvorbis. They're never freed or manipulated directly */ __android_log_print(ANDROID_LOG_INFO, "VorbisEncoder", "Completed encoding."); stopEncodeFeed(env, &encoderDataFeed, &stopMethodId); + + //Clean up encode buffers + (*env)->DeleteLocalRef(env, jByteArrayBuffer); + (*env)->DeleteLocalRef(env, jByteArrayWriteBuffer); + return SUCCESS; } diff --git a/jni/libvorbis-jni/org_xiph_vorbis_encoder_VorbisEncoder.h b/jni/libvorbis-jni/org_xiph_vorbis_encoder_VorbisEncoder.h index c423933..ecd927c 100644 --- a/jni/libvorbis-jni/org_xiph_vorbis_encoder_VorbisEncoder.h +++ b/jni/libvorbis-jni/org_xiph_vorbis_encoder_VorbisEncoder.h @@ -23,10 +23,10 @@ void startEncodeFeed(JNIEnv *env, jobject *vorbisDataFeed, jmethodID* startMetho void stopEncodeFeed(JNIEnv *env, jobject* vorbisDataFeed, jmethodID* stopMethodId); //Reads pcm data from the jni callback -long readPCMDataFromEncoderDataFeed(JNIEnv *env, jobject* encoderDataFeed, jmethodID* readPCMDataMethodId, char* buffer, int length); +long readPCMDataFromEncoderDataFeed(JNIEnv *env, jobject* encoderDataFeed, jmethodID* readPCMDataMethodId, char* buffer, int length, jbyteArray* jByteArrayBuffer); //Writes the vorbis data to the Java layer -int writeVorbisDataToEncoderDataFeed(JNIEnv *env, jobject* encoderDataFeed, jmethodID* writeVorbisDataMethodId, char* buffer, int bytes); +int writeVorbisDataToEncoderDataFeed(JNIEnv *env, jobject* encoderDataFeed, jmethodID* writeVorbisDataMethodId, char* buffer, int bytes, jbyteArray* jByteArrayWriteBuffer); JNIEXPORT int JNICALL Java_org_xiph_vorbis_encoder_VorbisEncoder_startEncoding (JNIEnv *env, jclass cls, jlong sampleRate, jlong channels, jfloat quality, jobject encoderDataFeed); diff --git a/libs/armeabi/libvorbis-jni.so b/libs/armeabi/libvorbis-jni.so index 135cff935d627c33d5a348507f0b766c846e4a7f..12df41dd930f3ff1a90b54d3d218d76db3247a69 100755 GIT binary patch delta 5654 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 delta 5543 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