Reuse buffers for video and audio renderer callbacks to prevent excessive object allocation during stream

This commit is contained in:
Cameron Gutman 2017-05-18 13:03:36 -07:00
parent 0ce1e1be27
commit b3a1938c1d
3 changed files with 34 additions and 27 deletions

View File

@ -3,7 +3,7 @@ package com.limelight.nvstream.av.video;
public abstract class VideoDecoderRenderer { public abstract class VideoDecoderRenderer {
public abstract int setup(int format, int width, int height, int redrawRate); public abstract int setup(int format, int width, int height, int redrawRate);
public abstract int submitDecodeUnit(byte[] frameData); public abstract int submitDecodeUnit(byte[] frameData, int frameLength);
public abstract void cleanup(); public abstract void cleanup();

View File

@ -48,9 +48,9 @@ public class MoonBridge {
} }
} }
public static int bridgeDrSubmitDecodeUnit(byte[] frameData) { public static int bridgeDrSubmitDecodeUnit(byte[] frameData, int frameLength) {
if (videoRenderer != null) { if (videoRenderer != null) {
return videoRenderer.submitDecodeUnit(frameData); return videoRenderer.submitDecodeUnit(frameData, frameLength);
} }
else { else {
return DR_OK; return DR_OK;

View File

@ -28,6 +28,8 @@ static jmethodID BridgeClConnectionStartedMethod;
static jmethodID BridgeClConnectionTerminatedMethod; static jmethodID BridgeClConnectionTerminatedMethod;
static jmethodID BridgeClDisplayMessageMethod; static jmethodID BridgeClDisplayMessageMethod;
static jmethodID BridgeClDisplayTransientMessageMethod; static jmethodID BridgeClDisplayTransientMessageMethod;
static jbyteArray DecodedFrameBuffer;
static jbyteArray DecodedAudioBuffer;
void DetachThread(void* context) { void DetachThread(void* context) {
(*JVM)->DetachCurrentThread(JVM); (*JVM)->DetachCurrentThread(JVM);
@ -71,7 +73,7 @@ Java_com_limelight_nvstream_jni_MoonBridge_init(JNIEnv *env, jobject class) {
GlobalBridgeClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/limelight/nvstream/jni/MoonBridge")); GlobalBridgeClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/limelight/nvstream/jni/MoonBridge"));
BridgeDrSetupMethod = (*env)->GetStaticMethodID(env, class, "bridgeDrSetup", "(IIII)I"); BridgeDrSetupMethod = (*env)->GetStaticMethodID(env, class, "bridgeDrSetup", "(IIII)I");
BridgeDrCleanupMethod = (*env)->GetStaticMethodID(env, class, "bridgeDrCleanup", "()V"); BridgeDrCleanupMethod = (*env)->GetStaticMethodID(env, class, "bridgeDrCleanup", "()V");
BridgeDrSubmitDecodeUnitMethod = (*env)->GetStaticMethodID(env, class, "bridgeDrSubmitDecodeUnit", "([B)I"); BridgeDrSubmitDecodeUnitMethod = (*env)->GetStaticMethodID(env, class, "bridgeDrSubmitDecodeUnit", "([BI)I");
BridgeArInitMethod = (*env)->GetStaticMethodID(env, class, "bridgeArInit", "(I)I"); BridgeArInitMethod = (*env)->GetStaticMethodID(env, class, "bridgeArInit", "(I)I");
BridgeArCleanupMethod = (*env)->GetStaticMethodID(env, class, "bridgeArCleanup", "()V"); BridgeArCleanupMethod = (*env)->GetStaticMethodID(env, class, "bridgeArCleanup", "()V");
BridgeArPlaySampleMethod = (*env)->GetStaticMethodID(env, class, "bridgeArPlaySample", "([B)V"); BridgeArPlaySampleMethod = (*env)->GetStaticMethodID(env, class, "bridgeArPlaySample", "([B)V");
@ -94,15 +96,23 @@ int BridgeDrSetup(int videoFormat, int width, int height, int redrawRate, void*
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSetupMethod, videoFormat, width, height, redrawRate); err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSetupMethod, videoFormat, width, height, redrawRate);
if ((*env)->ExceptionCheck(env)) { if ((*env)->ExceptionCheck(env)) {
err = -1; return -1;
}
else if (err != 0) {
return err;
} }
return err; // Use a 32K frame buffer that will increase if needed
DecodedFrameBuffer = (*env)->NewGlobalRef(env, (*env)->NewByteArray(env, 32768));
return 0;
} }
void BridgeDrCleanup(void) { void BridgeDrCleanup(void) {
JNIEnv* env = GetThreadEnv(); JNIEnv* env = GetThreadEnv();
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
if ((*env)->ExceptionCheck(env)) { if ((*env)->ExceptionCheck(env)) {
return; return;
} }
@ -117,27 +127,25 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
return DR_OK; return DR_OK;
} }
jbyteArray dataRef = (*env)->NewByteArray(env, decodeUnit->fullLength); // Increase the size of our frame data buffer if our frame won't fit
jbyte* data = (*env)->GetByteArrayElements(env, dataRef, 0); if ((*env)->GetArrayLength(env, DecodedFrameBuffer) < decodeUnit->fullLength) {
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
DecodedFrameBuffer = (*env)->NewGlobalRef(env, (*env)->NewByteArray(env, decodeUnit->fullLength));
}
PLENTRY currentEntry; PLENTRY currentEntry;
int offset; int offset;
currentEntry = decodeUnit->bufferList; currentEntry = decodeUnit->bufferList;
offset = 0; offset = 0;
while (currentEntry != NULL) { while (currentEntry != NULL) {
memcpy(&data[offset], currentEntry->data, currentEntry->length); (*env)->SetByteArrayRegion(env, DecodedFrameBuffer, offset, currentEntry->length, (jbyte*)currentEntry->data);
offset += currentEntry->length; offset += currentEntry->length;
currentEntry = currentEntry->next; currentEntry = currentEntry->next;
} }
// We must release the array elements first to ensure the data is copied before the callback return (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod, DecodedFrameBuffer, decodeUnit->fullLength);
(*env)->ReleaseByteArrayElements(env, dataRef, data, 0);
int ret = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod, dataRef);
(*env)->DeleteLocalRef(env, dataRef);
return ret;
} }
int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) {
@ -164,6 +172,9 @@ int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusCon
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArCleanupMethod); (*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArCleanupMethod);
return -1; return -1;
} }
// We know ahead of time what the buffer size will be for decoded audio, so pre-allocate it
DecodedAudioBuffer = (*env)->NewGlobalRef(env, (*env)->NewByteArray(env, opusConfig->channelCount * PCM_FRAME_SIZE * sizeof(short)));
} }
return err; return err;
@ -172,10 +183,9 @@ int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusCon
void BridgeArCleanup() { void BridgeArCleanup() {
JNIEnv* env = GetThreadEnv(); JNIEnv* env = GetThreadEnv();
if (Decoder != NULL) { opus_multistream_decoder_destroy(Decoder);
opus_multistream_decoder_destroy(Decoder);
Decoder = NULL; (*env)->DeleteGlobalRef(env, DecodedAudioBuffer);
}
if ((*env)->ExceptionCheck(env)) { if ((*env)->ExceptionCheck(env)) {
return; return;
@ -191,8 +201,7 @@ void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
return; return;
} }
jbyteArray decodedDataRef = (*env)->NewByteArray(env, OpusConfig.channelCount * PCM_FRAME_SIZE * sizeof(short)); jbyte* decodedData = (*env)->GetByteArrayElements(env, DecodedAudioBuffer, 0);
jbyte* decodedData = (*env)->GetByteArrayElements(env, decodedDataRef, 0);
int decodeLen = opus_multistream_decode(Decoder, int decodeLen = opus_multistream_decode(Decoder,
(const unsigned char*)sampleData, (const unsigned char*)sampleData,
@ -202,16 +211,14 @@ void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
0); 0);
if (decodeLen > 0) { if (decodeLen > 0) {
// We must release the array elements first to ensure the data is copied before the callback // We must release the array elements first to ensure the data is copied before the callback
(*env)->ReleaseByteArrayElements(env, decodedDataRef, decodedData, 0); (*env)->ReleaseByteArrayElements(env, DecodedAudioBuffer, decodedData, 0);
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArPlaySampleMethod, decodedDataRef); (*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArPlaySampleMethod, DecodedAudioBuffer);
} }
else { else {
// We can abort here to avoid the copy back since no data was modified // We can abort here to avoid the copy back since no data was modified
(*env)->ReleaseByteArrayElements(env, decodedDataRef, decodedData, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, DecodedAudioBuffer, decodedData, JNI_ABORT);
} }
(*env)->DeleteLocalRef(env, decodedDataRef);
} }
void BridgeClStageStarting(int stage) { void BridgeClStageStarting(int stage) {