From 45664dac2aaa1920a145c4ea233f219a297b0fa7 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 21 Nov 2013 08:38:49 -0500 Subject: [PATCH] Draw directly to the surface buffer. Improve amount of decoding and rendering that can be done in parallel. Add performance levels and choose them by cpuinfo. Improves Tegra 3 performance significantly. --- jni/nv_avc_dec/Android.mk | 2 +- jni/nv_avc_dec/nv_avc_dec.c | 169 +++++++++++------- jni/nv_avc_dec/nv_avc_dec.h | 7 +- jni/nv_avc_dec/nv_avc_dec_jni.c | 31 +--- libs/armeabi-v7a/libnv_avc_dec.so | Bin 17700 -> 17700 bytes libs/x86/libnv_avc_dec.so | Bin 9568 -> 9580 bytes src/com/limelight/Game.java | 7 +- .../nvstream/av/video/AvcDecoder.java | 7 +- .../nvstream/av/video/CpuDecoderRenderer.java | 115 +++++++++--- 9 files changed, 218 insertions(+), 120 deletions(-) diff --git a/jni/nv_avc_dec/Android.mk b/jni/nv_avc_dec/Android.mk index 7aaed9f4..c11bc583 100644 --- a/jni/nv_avc_dec/Android.mk +++ b/jni/nv_avc_dec/Android.mk @@ -9,7 +9,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := nv_avc_dec LOCAL_SRC_FILES := nv_avc_dec.c nv_avc_dec_jni.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/ffmpeg/$(TARGET_ARCH_ABI)/include -LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog +LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -landroid # Link to ffmpeg libraries LOCAL_SHARED_LIBRARIES := libavcodec libavformat libswscale libavutil libavfilter libwsresample diff --git a/jni/nv_avc_dec/nv_avc_dec.c b/jni/nv_avc_dec/nv_avc_dec.c index f66c5927..8758318e 100644 --- a/jni/nv_avc_dec/nv_avc_dec.c +++ b/jni/nv_avc_dec/nv_avc_dec.c @@ -5,20 +5,31 @@ #include #include "nv_avc_dec.h" +#include +#include + AVCodec* decoder; AVCodecContext* decoder_ctx; AVFrame* yuv_frame; -AVFrame* tmp_frame; AVFrame* rgb_frame; +AVFrame* rnd_frame; +AVFrame* dec_frame; pthread_mutex_t mutex; char* rgb_frame_buf; -int picture_valid; -int rgb_dirty; struct SwsContext* scaler_ctx; +int picture_new; + +#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 // This function must be called before // any other decoding functions -int nv_avc_init(int width, int height) { +int nv_avc_init(int width, int height, int perf_lvl) { int err; pthread_mutex_init(&mutex, NULL); @@ -44,12 +55,23 @@ int nv_avc_init(int width, int height) { // Show frames even before a reference frame decoder_ctx->flags2 |= CODEC_FLAG2_SHOW_ALL; - // Skip the loop filter for performance reasons - decoder_ctx->skip_loop_filter = AVDISCARD_ALL; + if (perf_lvl <= LOW_PERF) { + // Skip the loop filter for performance reasons + decoder_ctx->skip_loop_filter = AVDISCARD_ALL; + } - // Run 2 threads for decoding - decoder_ctx->thread_count = 2; - decoder_ctx->thread_type = FF_THREAD_FRAME; + 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 { + // Use low delay single threaded encoding + decoder_ctx->flags |= CODEC_FLAG_LOW_DELAY; + } decoder_ctx->width = width; decoder_ctx->height = height; @@ -62,13 +84,13 @@ int nv_avc_init(int width, int height) { return err; } - tmp_frame = av_frame_alloc(); - if (tmp_frame == NULL) { + dec_frame = av_frame_alloc(); + if (dec_frame == NULL) { __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", "Couldn't allocate frame"); return -1; } - + rgb_frame = av_frame_alloc(); if (rgb_frame == NULL) { __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", @@ -76,16 +98,16 @@ int nv_avc_init(int width, int height) { return -1; } - rgb_frame_buf = (char*)av_malloc(nv_avc_get_rgb_frame_size()); + rgb_frame_buf = (char*)av_malloc(width * height * BYTES_PER_PIXEL); if (rgb_frame_buf == NULL) { __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", "Couldn't allocate picture"); return -1; } - + err = avpicture_fill((AVPicture*)rgb_frame, rgb_frame_buf, - AV_PIX_FMT_RGB32, + RENDER_PIX_FMT, decoder_ctx->width, decoder_ctx->height); if (err < 0) { @@ -93,13 +115,13 @@ int nv_avc_init(int width, int height) { "Couldn't fill picture"); return err; } - + scaler_ctx = sws_getContext(decoder_ctx->width, decoder_ctx->height, decoder_ctx->pix_fmt, decoder_ctx->width, decoder_ctx->height, - AV_PIX_FMT_RGB32, + RENDER_PIX_FMT, SWS_BICUBIC, NULL, NULL, NULL); if (scaler_ctx == NULL) { @@ -123,9 +145,13 @@ void nv_avc_destroy(void) { sws_freeContext(scaler_ctx); scaler_ctx = NULL; } - if (tmp_frame) { + if (dec_frame) { + av_frame_free(&dec_frame); + dec_frame = NULL; + } + if (yuv_frame) { av_frame_free(&yuv_frame); - tmp_frame = NULL; + yuv_frame = NULL; } if (rgb_frame) { av_frame_free(&rgb_frame); @@ -135,32 +161,43 @@ void nv_avc_destroy(void) { av_free(rgb_frame_buf); rgb_frame_buf = NULL; } + if (rnd_frame) { + av_frame_free(&rnd_frame); + rnd_frame = NULL; + } pthread_mutex_destroy(&mutex); } -// The decoded frame is ARGB -// Returns 1 on success, 0 on failure -int nv_avc_get_current_frame(char* rgbframe, int size) { +void nv_avc_redraw(JNIEnv *env, jobject surface) { + ANativeWindow* window; + ANativeWindow_Buffer buffer; int err; - if (size != nv_avc_get_rgb_frame_size()) { - return 0; + // Free the old decoded frame + if (rnd_frame) { + av_frame_free(&rnd_frame); } pthread_mutex_lock(&mutex); - // Check if the RGB frame needs updating - if (rgb_dirty) { - // If the decoder doesn't have a new picture, we fail - if (!picture_valid) { - pthread_mutex_unlock(&mutex); - return 0; + // Check if there's a new frame + if (picture_new) { + // Clone the decoder's last frame + rnd_frame = av_frame_clone(yuv_frame); + + // The remaining processing can be done without the mutex + pthread_mutex_unlock(&mutex); + + if (rnd_frame == NULL) { + __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", + "Cloning failed"); + return; } // Convert the YUV image to RGB err = sws_scale(scaler_ctx, - yuv_frame->data, - yuv_frame->linesize, + rnd_frame->data, + rnd_frame->linesize, 0, decoder_ctx->height, rgb_frame->data, @@ -168,36 +205,42 @@ int nv_avc_get_current_frame(char* rgbframe, int size) { if (err != decoder_ctx->height) { __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", "Scaling failed"); - pthread_mutex_unlock(&mutex); - return 0; + return; } - // RGB frame is now clean - rgb_dirty = 0; + window = ANativeWindow_fromSurface(env, surface); + if (window == NULL) { + __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", + "Failed to get window from surface"); + return; + } + + // Lock down a render buffer + if (ANativeWindow_lock(window, &buffer, NULL) >= 0) { + // Draw the frame to the buffer + err = avpicture_layout((AVPicture*)rgb_frame, + RENDER_PIX_FMT, + decoder_ctx->width, + decoder_ctx->height, + buffer.bits, + decoder_ctx->width * + decoder_ctx->height * + BYTES_PER_PIXEL); + if (err < 0) { + __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", + "Picture fill failed"); + } + + // Draw the frame to the surface + ANativeWindow_unlockAndPost(window); + } + + ANativeWindow_release(window); } - - // The remaining processing can be done without the mutex - pthread_mutex_unlock(&mutex); - - err = avpicture_layout((AVPicture*)rgb_frame, - AV_PIX_FMT_RGB32, - decoder_ctx->width, - decoder_ctx->height, - rgbframe, - size); - if (err < 0) { - __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", - "Picture fill failed"); - return 0; + else { + pthread_mutex_unlock(&mutex); + rnd_frame = NULL; } - - return 1; -} - -int nv_avc_get_rgb_frame_size(void) { - return avpicture_get_size(AV_PIX_FMT_RGB32, - decoder_ctx->width, - decoder_ctx->height); } // packets must be decoded in order @@ -218,7 +261,7 @@ int nv_avc_decode(unsigned char* indata, int inlen) { while (pkt.size > 0) { err = avcodec_decode_video2( decoder_ctx, - tmp_frame, + dec_frame, &got_pic, &pkt); if (err < 0) { @@ -237,14 +280,12 @@ int nv_avc_decode(unsigned char* indata, int inlen) { } // Clone a new frame - yuv_frame = av_frame_clone(tmp_frame); + yuv_frame = av_frame_clone(dec_frame); if (yuv_frame) { - // If we got a new picture, the RGB frame needs refreshing - picture_valid = 1; - rgb_dirty = 1; + picture_new = 1; } else { - picture_valid = 0; + picture_new = 0; } pthread_mutex_unlock(&mutex); diff --git a/jni/nv_avc_dec/nv_avc_dec.h b/jni/nv_avc_dec/nv_avc_dec.h index ea70ce38..d53d5425 100644 --- a/jni/nv_avc_dec/nv_avc_dec.h +++ b/jni/nv_avc_dec/nv_avc_dec.h @@ -1,5 +1,6 @@ -int nv_avc_init(int width, int height); +#include + +int nv_avc_init(int width, int height, int perf_lvl); void nv_avc_destroy(void); -int nv_avc_get_current_frame(char* yuvframe, int size); -int nv_avc_get_frame_size(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 b8d3442b..e438c703 100644 --- a/jni/nv_avc_dec/nv_avc_dec_jni.c +++ b/jni/nv_avc_dec/nv_avc_dec_jni.c @@ -6,8 +6,10 @@ // This function must be called before // any other decoding functions JNIEXPORT jint JNICALL -Java_com_limelight_nvstream_av_video_AvcDecoder_init(JNIEnv *env, jobject this, jint width, jint height) { - return nv_avc_init(width, height); +Java_com_limelight_nvstream_av_video_AvcDecoder_init(JNIEnv *env, jobject this, jint width, + jint height, jint perflvl) +{ + return nv_avc_init(width, height, perflvl); } // This function must be called after @@ -17,27 +19,10 @@ Java_com_limelight_nvstream_av_video_AvcDecoder_destroy(JNIEnv *env, jobject thi nv_avc_destroy(); } -// The decoded frame is ARGB -// Returns 1 on success, 0 on failure -JNIEXPORT jboolean JNICALL -Java_com_limelight_nvstream_av_video_AvcDecoder_getCurrentFrame(JNIEnv *env, jobject this, - jintArray rgbframe, jint sizeints) -{ - jint* jni_rgbframe; - jboolean ret; - - jni_rgbframe = (*env)->GetIntArrayElements(env, rgbframe, 0); - - ret = (nv_avc_get_current_frame((char*)jni_rgbframe, sizeints*4) != 0) ? JNI_TRUE : JNI_FALSE; - - (*env)->ReleaseIntArrayElements(env, rgbframe, jni_rgbframe, 0); - - return ret; -} - -JNIEXPORT jint JNICALL -Java_com_limelight_nvstream_av_video_AvcDecoder_getFrameSize(JNIEnv *env, jobject this) { - return nv_avc_get_rgb_frame_size() / 4; +// This function redraws the surface +JNIEXPORT void JNICALL +Java_com_limelight_nvstream_av_video_AvcDecoder_redraw(JNIEnv *env, jobject this, jobject surface) { + nv_avc_redraw(env, surface); } // packets must be decoded in order diff --git a/libs/armeabi-v7a/libnv_avc_dec.so b/libs/armeabi-v7a/libnv_avc_dec.so index 75fa000199f3f2275af1f19a7b97177c6e4fd92b..2d1c289d1afab7eaef53641c405239895c88efa5 100644 GIT binary patch delta 5805 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 delta 5754 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 diff --git a/libs/x86/libnv_avc_dec.so b/libs/x86/libnv_avc_dec.so index 27dc7c4b1836eac42371c752de0ea42421520d8d..44b86be8267e1210f70e6e84684a64013820d746 100644 GIT binary patch literal 9580 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

5l9Xg^Z73%fN`uRkoXn(@9FPD5 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 0) { + // Sleep until the frame should be rendered + try { + Thread.sleep(diff); + } catch (InterruptedException e) { + return; + } } - if (!AvcDecoder.getCurrentFrame(frameBuffer, frameBuffer.length)) - continue; - - // Draw the new bitmap to the canvas - Canvas c = renderTarget.lockCanvas(null); - c.drawBitmap(frameBuffer, 0, width, 0, 0, width, height, false, null); - renderTarget.unlockCanvasAndPost(c); + nextFrameTime = computePresentationTimeMs(frameRateTarget); + AvcDecoder.redraw(renderTarget); } } }; rendererThread.start(); } + + private long computePresentationTimeMs(int frameRate) { + return System.currentTimeMillis() + (1000 / frameRate); + } @Override public void stop() {