diff --git a/jni/nv_avc_dec/nv_avc_dec.c b/jni/nv_avc_dec/nv_avc_dec.c index 525f3567..4366ce11 100644 --- a/jni/nv_avc_dec/nv_avc_dec.c +++ b/jni/nv_avc_dec/nv_avc_dec.c @@ -20,15 +20,24 @@ struct SwsContext* scaler_ctx; #define RENDER_PIX_FMT AV_PIX_FMT_RGBA #define BYTES_PER_PIXEL 4 -#define VERY_LOW_PERF 0 -#define LOW_PERF 1 -#define MED_PERF 2 -#define HIGH_PERF 3 +// Disables the deblocking filter at the cost of image quality +#define DISABLE_LOOP_FILTER 0x1 +// Uses the low latency decode flag (disables multithreading) +#define LOW_LATENCY_DECODE 0x2 +// Threads process each slice, rather than each frame +#define SLICE_THREADING 0x4 +// Uses nonstandard speedup tricks +#define FAST_DECODE 0x8 +// Uses bilinear filtering instead of bicubic +#define BILINEAR_FILTERING 0x10 +// Uses a faster bilinear filtering with lower image quality +#define FAST_BILINEAR_FILTERING 0x20 // This function must be called before // any other decoding functions -int nv_avc_init(int width, int height, int perf_lvl) { +int nv_avc_init(int width, int height, int perf_lvl, int thread_count) { int err; + int filtering; pthread_mutex_init(&mutex, NULL); @@ -53,24 +62,25 @@ int nv_avc_init(int width, int height, int perf_lvl) { // Show frames even before a reference frame decoder_ctx->flags2 |= CODEC_FLAG2_SHOW_ALL; - if (perf_lvl <= LOW_PERF) { + if (perf_lvl & DISABLE_LOOP_FILTER) { // Skip the loop filter for performance reasons decoder_ctx->skip_loop_filter = AVDISCARD_ALL; } - if (perf_lvl <= MED_PERF) { - // Run 2 threads for decoding - decoder_ctx->thread_count = 2; - decoder_ctx->thread_type = FF_THREAD_FRAME; - - // Use some tricks to make things faster - decoder_ctx->flags2 |= CODEC_FLAG2_FAST; - } - else { + if (perf_lvl & LOW_LATENCY_DECODE) { // Use low delay single threaded encoding decoder_ctx->flags |= CODEC_FLAG_LOW_DELAY; } + if (perf_lvl & SLICE_THREADING) { + decoder_ctx->thread_type = FF_THREAD_SLICE; + } + else { + decoder_ctx->thread_type = FF_THREAD_FRAME; + } + + decoder_ctx->thread_count = thread_count; + decoder_ctx->width = width; decoder_ctx->height = height; decoder_ctx->pix_fmt = PIX_FMT_YUV420P; @@ -114,13 +124,23 @@ int nv_avc_init(int width, int height, int perf_lvl) { return err; } + if (perf_lvl & FAST_BILINEAR_FILTERING) { + filtering = SWS_FAST_BILINEAR; + } + else if (perf_lvl & BILINEAR_FILTERING) { + filtering = SWS_BILINEAR; + } + else { + filtering = SWS_BICUBIC; + } + scaler_ctx = sws_getContext(decoder_ctx->width, decoder_ctx->height, decoder_ctx->pix_fmt, decoder_ctx->width, decoder_ctx->height, RENDER_PIX_FMT, - SWS_BICUBIC, + filtering, NULL, NULL, NULL); if (scaler_ctx == NULL) { __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", diff --git a/jni/nv_avc_dec/nv_avc_dec.h b/jni/nv_avc_dec/nv_avc_dec.h index d53d5425..bc47b021 100644 --- a/jni/nv_avc_dec/nv_avc_dec.h +++ b/jni/nv_avc_dec/nv_avc_dec.h @@ -1,6 +1,6 @@ #include -int nv_avc_init(int width, int height, int perf_lvl); +int nv_avc_init(int width, int height, int perf_lvl, int thread_count); void nv_avc_destroy(void); void nv_avc_redraw(JNIEnv *env, jobject surface); int nv_avc_decode(unsigned char* indata, int inlen); diff --git a/jni/nv_avc_dec/nv_avc_dec_jni.c b/jni/nv_avc_dec/nv_avc_dec_jni.c index e438c703..70f165b7 100644 --- a/jni/nv_avc_dec/nv_avc_dec_jni.c +++ b/jni/nv_avc_dec/nv_avc_dec_jni.c @@ -7,9 +7,9 @@ // any other decoding functions JNIEXPORT jint JNICALL Java_com_limelight_nvstream_av_video_AvcDecoder_init(JNIEnv *env, jobject this, jint width, - jint height, jint perflvl) + jint height, jint perflvl, jint threadcount) { - return nv_avc_init(width, height, perflvl); + return nv_avc_init(width, height, perflvl, threadcount); } // This function must be called after diff --git a/libs/armeabi-v7a/libnv_avc_dec.so b/libs/armeabi-v7a/libnv_avc_dec.so index ad485f7c..f9e6b5bc 100644 Binary files a/libs/armeabi-v7a/libnv_avc_dec.so and b/libs/armeabi-v7a/libnv_avc_dec.so differ diff --git a/libs/x86/libnv_avc_dec.so b/libs/x86/libnv_avc_dec.so index 8c52cb7f..affff12f 100644 Binary files a/libs/x86/libnv_avc_dec.so and b/libs/x86/libnv_avc_dec.so differ diff --git a/src/com/limelight/nvstream/av/video/AvcDecoder.java b/src/com/limelight/nvstream/av/video/AvcDecoder.java index a886bb0f..c4299080 100644 --- a/src/com/limelight/nvstream/av/video/AvcDecoder.java +++ b/src/com/limelight/nvstream/av/video/AvcDecoder.java @@ -15,7 +15,20 @@ public class AvcDecoder { System.loadLibrary("nv_avc_dec"); } - public static native int init(int width, int height, int perflvl); + /** Disables the deblocking filter at the cost of image quality */ + public static final int DISABLE_LOOP_FILTER = 0x1; + /** Uses the low latency decode flag (disables multithreading) */ + public static final int LOW_LATENCY_DECODE = 0x2; + /** Threads process each slice, rather than each frame */ + public static final int SLICE_THREADING = 0x4; + /** Uses nonstandard speedup tricks */ + public static final int FAST_DECODE = 0x8; + /** Uses bilinear filtering instead of bicubic */ + public static final int BILINEAR_FILTERING = 0x10; + /** Uses a faster bilinear filtering with lower image quality */ + public static final int FAST_BILINEAR_FILTERING = 0x20; + + public static native int init(int width, int height, int perflvl, int threadcount); public static native void destroy(); public static native void redraw(Surface surface); public static native int decode(byte[] indata, int inoff, int inlen); diff --git a/src/com/limelight/nvstream/av/video/CpuDecoderRenderer.java b/src/com/limelight/nvstream/av/video/CpuDecoderRenderer.java index 9b724fc2..ca3ad89d 100644 --- a/src/com/limelight/nvstream/av/video/CpuDecoderRenderer.java +++ b/src/com/limelight/nvstream/av/video/CpuDecoderRenderer.java @@ -73,7 +73,25 @@ public class CpuDecoderRenderer implements DecoderRenderer { this.renderTarget = renderTarget; this.perfLevel = findOptimalPerformanceLevel(); - int err = AvcDecoder.init(width, height, perfLevel); + int flags = 0; + switch (perfLevel) { + case LOW_PERF: + flags = AvcDecoder.DISABLE_LOOP_FILTER | + AvcDecoder.FAST_BILINEAR_FILTERING | + AvcDecoder.FAST_DECODE | + AvcDecoder.LOW_LATENCY_DECODE; + break; + case MED_PERF: + flags = AvcDecoder.LOW_LATENCY_DECODE | + AvcDecoder.FAST_DECODE | + AvcDecoder.BILINEAR_FILTERING; + break; + case HIGH_PERF: + flags = AvcDecoder.LOW_LATENCY_DECODE; + break; + } + + int err = AvcDecoder.init(width, height, flags, 2); if (err != 0) { throw new IllegalStateException("AVC decoder initialization failure: "+err); }