From b0bb8b685cd855644ef547a8a7bff8de814c3861 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Fri, 29 Nov 2013 21:06:35 -0600 Subject: [PATCH] Further optimize the JNI code for faster H264 decoding. Add an experimental RenderScript renderer. --- jni/nv_avc_dec/nv_avc_dec.c | 312 +++++++++++------- jni/nv_avc_dec/nv_avc_dec.h | 9 +- jni/nv_avc_dec/nv_avc_dec_jni.c | 61 +++- libs/armeabi-v7a/libnv_avc_dec.so | Bin 17700 -> 17700 bytes libs/x86/libnv_avc_dec.so | Bin 9576 -> 13664 bytes src/com/limelight/nvstream/NvConnection.java | 2 +- src/com/limelight/nvstream/NvVideoStream.java | 11 +- .../nvstream/av/video/DecoderRenderer.java | 3 +- .../av/video/MediaCodecDecoderRenderer.java | 3 +- .../av/video/{ => cpu}/AvcDecoder.java | 15 +- .../video/{ => cpu}/CpuDecoderRenderer.java | 36 +- .../nvstream/av/video/cpu/RsRenderer.java | 36 ++ 12 files changed, 347 insertions(+), 141 deletions(-) rename src/com/limelight/nvstream/av/video/{ => cpu}/AvcDecoder.java (68%) rename src/com/limelight/nvstream/av/video/{ => cpu}/CpuDecoderRenderer.java (82%) create mode 100644 src/com/limelight/nvstream/av/video/cpu/RsRenderer.java diff --git a/jni/nv_avc_dec/nv_avc_dec.c b/jni/nv_avc_dec/nv_avc_dec.c index 4366ce11..3d392d7d 100644 --- a/jni/nv_avc_dec/nv_avc_dec.c +++ b/jni/nv_avc_dec/nv_avc_dec.c @@ -8,14 +8,20 @@ #include #include +// General decoder and renderer state +AVPacket pkt; AVCodec* decoder; AVCodecContext* decoder_ctx; AVFrame* yuv_frame; -AVFrame* rgb_frame; AVFrame* dec_frame; pthread_mutex_t mutex; + +// Color conversion and rendering +AVFrame* rgb_frame; char* rgb_frame_buf; struct SwsContext* scaler_ctx; +ANativeWindow* window; + #define RENDER_PIX_FMT AV_PIX_FMT_RGBA #define BYTES_PER_PIXEL 4 @@ -32,6 +38,8 @@ struct SwsContext* scaler_ctx; #define BILINEAR_FILTERING 0x10 // Uses a faster bilinear filtering with lower image quality #define FAST_BILINEAR_FILTERING 0x20 +// Disables color conversion (output is NV21) +#define NO_COLOR_CONVERSION 0x40 // This function must be called before // any other decoding functions @@ -44,6 +52,8 @@ int nv_avc_init(int width, int height, int perf_lvl, int thread_count) { // Initialize the avcodec library and register codecs av_log_set_level(AV_LOG_QUIET); avcodec_register_all(); + + av_init_packet(&pkt); decoder = avcodec_find_decoder(AV_CODEC_ID_H264); if (decoder == NULL) { @@ -98,54 +108,56 @@ int nv_avc_init(int width, int height, int perf_lvl, int thread_count) { "Couldn't allocate frame"); return -1; } + + if (!(perf_lvl & NO_COLOR_CONVERSION)) { + rgb_frame = av_frame_alloc(); + if (rgb_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", - "Couldn't allocate frame"); - return -1; - } + 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; + } - 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, + RENDER_PIX_FMT, + decoder_ctx->width, + decoder_ctx->height); + if (err < 0) { + __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", + "Couldn't fill picture"); + return err; + } - err = avpicture_fill((AVPicture*)rgb_frame, - rgb_frame_buf, - RENDER_PIX_FMT, - decoder_ctx->width, - decoder_ctx->height); - if (err < 0) { - __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", - "Couldn't fill picture"); - 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; + } - 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, - filtering, - NULL, NULL, NULL); - if (scaler_ctx == NULL) { - __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", - "Couldn't get scaler context"); - return -1; + scaler_ctx = sws_getContext(decoder_ctx->width, + decoder_ctx->height, + decoder_ctx->pix_fmt, + decoder_ctx->width, + decoder_ctx->height, + RENDER_PIX_FMT, + filtering, + NULL, NULL, NULL); + if (scaler_ctx == NULL) { + __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", + "Couldn't get scaler context"); + return -1; + } } return 0; @@ -179,14 +191,15 @@ void nv_avc_destroy(void) { av_free(rgb_frame_buf); rgb_frame_buf = NULL; } + if (window) { + ANativeWindow_release(window); + window = NULL; + } pthread_mutex_destroy(&mutex); } -void nv_avc_redraw(JNIEnv *env, jobject surface) { - ANativeWindow* window; - ANativeWindow_Buffer buffer; - AVFrame *our_yuv_frame; - int err; +static AVFrame* dequeue_new_frame(void) { + AVFrame *our_yuv_frame = NULL; pthread_mutex_lock(&mutex); @@ -196,77 +209,149 @@ void nv_avc_redraw(JNIEnv *env, jobject surface) { // responsible for freeing it when we're done our_yuv_frame = yuv_frame; yuv_frame = NULL; + } - // The remaining processing can be done without the mutex - pthread_mutex_unlock(&mutex); + pthread_mutex_unlock(&mutex); - // Convert the YUV image to RGB - err = sws_scale(scaler_ctx, - our_yuv_frame->data, - our_yuv_frame->linesize, - 0, - decoder_ctx->height, - rgb_frame->data, - rgb_frame->linesize); - if (err != decoder_ctx->height) { - __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", - "Scaling failed"); - goto free_frame_and_return; - } + return our_yuv_frame; +} - window = ANativeWindow_fromSurface(env, surface); - if (window == NULL) { - __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", - "Failed to get window from surface"); - goto free_frame_and_return; - } +static int update_rgb_frame(void) { + AVFrame *our_yuv_frame; + int err; + our_yuv_frame = dequeue_new_frame(); + if (our_yuv_frame == NULL) { + return 0; + } + + // Convert the YUV image to RGB + err = sws_scale(scaler_ctx, + our_yuv_frame->data, + our_yuv_frame->linesize, + 0, + decoder_ctx->height, + rgb_frame->data, + rgb_frame->linesize); + + av_frame_free(&our_yuv_frame); + + if (err != decoder_ctx->height) { + __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", + "Scaling failed"); + return 0; + } + + return 1; +} + +static int render_rgb_to_buffer(char* buffer, int size) { + int err; + + // Draw the frame to the buffer + err = avpicture_layout((AVPicture*)rgb_frame, + RENDER_PIX_FMT, + decoder_ctx->width, + decoder_ctx->height, + buffer, + size); + if (err < 0) { + __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", + "Picture fill failed"); + return 0; + } + + return 1; +} + +int nv_avc_get_raw_frame(char* buffer, int size) { + AVFrame *our_yuv_frame; + int err; + + our_yuv_frame = dequeue_new_frame(); + if (our_yuv_frame == NULL) { + return 0; + } + + err = avpicture_layout((AVPicture*)our_yuv_frame, + decoder_ctx->pix_fmt, + decoder_ctx->width, + decoder_ctx->height, + buffer, + size); + + av_frame_free(&our_yuv_frame); + + return (err >= 0); +} + +int nv_avc_get_rgb_frame(char* buffer, int size) { + return (update_rgb_frame() && render_rgb_to_buffer(buffer, size)); +} + +int nv_avc_set_render_target(JNIEnv *env, jobject surface) { + // Release the old window + if (window) { + ANativeWindow_release(window); + window = NULL; + } + + // If no new surface was supplied, we're done + if (surface == NULL) { + return 1; + } + + // Get a window from the surface + window = ANativeWindow_fromSurface(env, surface); + if (window == NULL) { + __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", + "Failed to get window from surface"); + return 0; + } + + return 1; +} + +int nv_avc_redraw(void) { + ANativeWindow_Buffer buffer; + int ret = 0; + + // Check if there's a new frame + if (update_rgb_frame()) { // 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, + if (render_rgb_to_buffer(buffer.bits, decoder_ctx->width * decoder_ctx->height * - BYTES_PER_PIXEL); - if (err < 0) { - __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", - "Picture fill failed"); + BYTES_PER_PIXEL)) { + // A new frame will be drawn + ret = 1; } // Draw the frame to the surface ANativeWindow_unlockAndPost(window); } + } + + return ret; +} - ANativeWindow_release(window); - - free_frame_and_return: - av_frame_free(&our_yuv_frame); - } - else { - pthread_mutex_unlock(&mutex); - } +int nv_avc_get_input_padding_size(void) { + return FF_INPUT_BUFFER_PADDING_SIZE; } // packets must be decoded in order +// indata must be inlen + FF_INPUT_BUFFER_PADDING_SIZE in length int nv_avc_decode(unsigned char* indata, int inlen) { int err; - AVPacket pkt; - int got_pic; + int got_pic = 0; - err = av_new_packet(&pkt, inlen); - if (err < 0) { - __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", - "Failed to allocate packet"); - return err; - } - - memcpy(pkt.data, indata, inlen); + pkt.data = indata; + pkt.size = inlen; while (pkt.size > 0) { + got_pic = 0; err = avcodec_decode_video2( decoder_ctx, dec_frame, @@ -275,29 +360,28 @@ int nv_avc_decode(unsigned char* indata, int inlen) { if (err < 0) { __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC", "Decode failed"); - pthread_mutex_unlock(&mutex); + got_pic = 0; break; } - if (got_pic) { - pthread_mutex_lock(&mutex); - - // Only clone this frame if the last frame was taken. - // This saves on extra copies for frames that don't get - // rendered. - if (yuv_frame == NULL) { - // Clone a new frame - yuv_frame = av_frame_clone(dec_frame); - } - - pthread_mutex_unlock(&mutex); - } - pkt.size -= err; pkt.data += err; } + + // Only copy the picture at the end of decoding the packet + if (got_pic) { + pthread_mutex_lock(&mutex); - av_free_packet(&pkt); + // Only clone this frame if the last frame was taken. + // This saves on extra copies for frames that don't get + // rendered. + if (yuv_frame == NULL) { + // Clone a new frame + yuv_frame = av_frame_clone(dec_frame); + } + + pthread_mutex_unlock(&mutex); + } return err < 0 ? err : 0; } diff --git a/jni/nv_avc_dec/nv_avc_dec.h b/jni/nv_avc_dec/nv_avc_dec.h index bc47b021..b67bc11c 100644 --- a/jni/nv_avc_dec/nv_avc_dec.h +++ b/jni/nv_avc_dec/nv_avc_dec.h @@ -2,5 +2,12 @@ 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_get_raw_frame(char* buffer, int size); + +int nv_avc_get_rgb_frame(char* buffer, int size); +int nv_avc_set_render_target(JNIEnv *env, jobject surface); +int nv_avc_redraw(void); + +int nv_avc_get_input_padding_size(void); 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 70f165b7..7b38f5d4 100644 --- a/jni/nv_avc_dec/nv_avc_dec_jni.c +++ b/jni/nv_avc_dec/nv_avc_dec_jni.c @@ -6,7 +6,7 @@ // 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, +Java_com_limelight_nvstream_av_video_cpu_AvcDecoder_init(JNIEnv *env, jobject this, jint width, jint height, jint perflvl, jint threadcount) { return nv_avc_init(width, height, perflvl, threadcount); @@ -15,20 +15,69 @@ Java_com_limelight_nvstream_av_video_AvcDecoder_init(JNIEnv *env, jobject this, // This function must be called after // decoding is finished JNIEXPORT void JNICALL -Java_com_limelight_nvstream_av_video_AvcDecoder_destroy(JNIEnv *env, jobject this) { +Java_com_limelight_nvstream_av_video_cpu_AvcDecoder_destroy(JNIEnv *env, jobject this) { nv_avc_destroy(); } +// fills the output buffer with a raw YUV frame +JNIEXPORT jboolean JNICALL +Java_com_limelight_nvstream_av_video_cpu_AvcDecoder_getRawFrame( + JNIEnv *env, jobject this, // JNI parameters + jbyteArray outdata, jint outlen) // Output data +{ + jint ret; + jbyte* jni_output_data; + + jni_output_data = (*env)->GetByteArrayElements(env, outdata, 0); + + ret = nv_avc_get_raw_frame(jni_output_data, outlen); + + (*env)->ReleaseByteArrayElements(env, outdata, jni_output_data, 0); + + return ret != 0 ? JNI_TRUE : JNI_FALSE; +} + +// fills the output buffer with an RGB frame +JNIEXPORT jboolean JNICALL +Java_com_limelight_nvstream_av_video_cpu_AvcDecoder_getRgbFrame( + JNIEnv *env, jobject this, // JNI parameters + jbyteArray outdata, jint outlen) // Output data +{ + jint ret; + jbyte* jni_output_data; + + jni_output_data = (*env)->GetByteArrayElements(env, outdata, 0); + + ret = nv_avc_get_rgb_frame(jni_output_data, outlen); + + (*env)->ReleaseByteArrayElements(env, outdata, jni_output_data, 0); + + return ret != 0 ? JNI_TRUE : JNI_FALSE; +} + +// This function sets the rendering target for redraw +JNIEXPORT jboolean JNICALL +Java_com_limelight_nvstream_av_video_cpu_AvcDecoder_setRenderTarget(JNIEnv *env, jobject this, jobject surface) { + return nv_avc_set_render_target(env, surface) != 0 ? JNI_TRUE : JNI_FALSE; +} + // 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); +JNIEXPORT jboolean JNICALL +Java_com_limelight_nvstream_av_video_cpu_AvcDecoder_redraw(JNIEnv *env, jobject this) { + return nv_avc_redraw() != 0 ? JNI_TRUE : JNI_FALSE; +} + +// This function returns the required input buffer padding +JNIEXPORT jint JNICALL +Java_com_limelight_nvstream_av_video_cpu_AvcDecoder_getInputPaddingSize(JNIEnv *env, jobject this) { + return nv_avc_get_input_padding_size(); } // packets must be decoded in order +// the input buffer must have proper padding // returns 0 on success, < 0 on error JNIEXPORT jint JNICALL -Java_com_limelight_nvstream_av_video_AvcDecoder_decode( +Java_com_limelight_nvstream_av_video_cpu_AvcDecoder_decode( JNIEnv *env, jobject this, // JNI parameters jbyteArray indata, jint inoff, jint inlen) { diff --git a/libs/armeabi-v7a/libnv_avc_dec.so b/libs/armeabi-v7a/libnv_avc_dec.so index f9e6b5bc1e60743afe815631a4a4a7f4fe077417..0b7e86b092de057148d3484c069f63caa6c62a9a 100644 GIT binary patch delta 6868 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 delta 5888 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 X!#kL#7SGqqUtjYvMRJ~Ji#z@Y!vG$Z diff --git a/libs/x86/libnv_avc_dec.so b/libs/x86/libnv_avc_dec.so index affff12fc086536ab6452e9d7e5dcf231dec19cb..af38168ef7b491b710b18b823b1af4a018d28d1e 100644 GIT binary patch literal 13664 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 literal 9576 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