[vlc-devel] [PATCH 2/2] Add support for the new Android JellyBean MediaCodec API

Martin Storsjö martin at martin.st
Mon Sep 24 15:44:33 CEST 2012


The MediaCodec API is a pretty thin layer on top of OMX, so
while it in principle doesn't gain us much, it is an official
API, while the OMX parts aren't. This API is a Java API, so
we have to use JNI to access it. While this is a few layers
extra, it is supposed to be a fixed API/ABI (contrary to the
IOMX layer which is unsupported in practice, but where the
ABI has been broken only between certain major releases).

This should in principle be enabled by default (without any
settings check box), since this is official public API and is
supposed to work. However in practice it might still be useful to
be able to disable/enable it at runtime, since there's currently
very little guarantees from Android about how to interpret the
decoded YUV data, and what pixel formats devices can use.
---
 configure.ac                             |   13 +
 modules/codec/Modules.am                 |    7 +-
 modules/codec/omxil/android_mediacodec.c |  469 ++++++++++++++++++++++++++++++
 3 files changed, 487 insertions(+), 2 deletions(-)
 create mode 100644 modules/codec/omxil/android_mediacodec.c

diff --git a/configure.ac b/configure.ac
index 8b4567a..d3148af 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3096,6 +3096,19 @@ fi
 
 
 dnl
+dnl  android mediacodec module
+dnl
+AC_ARG_ENABLE(mediacodec,
+  [  --enable-mediacodec   Android MediaCodec module (default disabled)])
+if test "${enable_mediacodec}" = "yes"; then
+  if test "${HAVE_ANDROID}" = "1"; then
+     VLC_ADD_PLUGIN([mediacodec])
+     VLC_ADD_LDFLAGS([mediacodec])
+  fi
+fi
+
+
+dnl
 dnl  iOS vout module
 dnl
 AC_ARG_ENABLE(ios-vout,
diff --git a/modules/codec/Modules.am b/modules/codec/Modules.am
index bcda85b..19052a3 100644
--- a/modules/codec/Modules.am
+++ b/modules/codec/Modules.am
@@ -143,8 +143,11 @@ libiomx_plugin_la_SOURCES = $(libomxil_plugin_la_SOURCES)
 libiomx_plugin_la_CPPFLAGS = $(libomxil_plugin_la_CPPFLAGS) -DUSE_IOMX
 libiomx_plugin_la_LIBADD = $(libomxil_plugin_la_LIBADD)
 
-libvlc_LTLIBRARIES += $(LTLIBomxil) $(LTLIBiomx)
-EXTRA_LTLIBRARIES += libomxil_plugin.la libiomx_plugin.la
+libmediacodec_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/omxil
+libmediacodec_plugin_la_SOURCES = omxil/android_mediacodec.c omxil/utils.c
+
+libvlc_LTLIBRARIES += $(LTLIBomxil) $(LTLIBiomx) $(LTLIBmediacodec)
+EXTRA_LTLIBRARIES += libomxil_plugin.la libiomx_plugin.la libmediacodec_plugin.la
 
 ### Windows DLL loader ###
 
diff --git a/modules/codec/omxil/android_mediacodec.c b/modules/codec/omxil/android_mediacodec.c
new file mode 100644
index 0000000..bf2c156
--- /dev/null
+++ b/modules/codec/omxil/android_mediacodec.c
@@ -0,0 +1,469 @@
+/*****************************************************************************
+ * android_mediacodec.c: Video decoder module using the Android MediaCodec API
+ *****************************************************************************
+ * Copyright (C) 2012 Martin Storsjo
+ *
+ * Authors: Martin Storsjo <martin at martin.st>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <jni.h>
+#include <stdint.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_codec.h>
+#include <vlc_block_helper.h>
+#include <vlc_cpu.h>
+
+#include "../h264_nal.h"
+#include <OMX_Core.h>
+#include <OMX_Component.h>
+#include "omxil_utils.h"
+
+extern JavaVM *myVm;
+
+struct decoder_sys_t
+{
+    uint32_t nal_size;
+
+    jobject codec;
+    jobject buffer_info;
+    jmethodID stop, flush, release;
+    jmethodID get_output_format, get_input_buffers, get_output_buffers;
+    jmethodID dequeue_input_buffer, dequeue_output_buffer, queue_input_buffer, release_output_buffer;
+    jmethodID get_integer;
+    jmethodID tostring;
+    jfieldID size_field, offset_field, pts_field;
+
+    jobject input_buffers, output_buffers;
+    int pixel_format;
+    int stride, slice_height;
+    int crop_top, crop_left;
+
+    int started;
+    int decoded;
+};
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int  OpenDecoder(vlc_object_t *);
+static void CloseDecoder(vlc_object_t *);
+
+static picture_t *DecodeVideo(decoder_t *, block_t **);
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+vlc_module_begin ()
+    set_description( N_("Video decoder using Android MediaCodec") )
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_VCODEC )
+    set_section( N_("Decoding") , NULL )
+    set_capability( "decoder", 0 ) /* Only enabled via commandline arguments */
+    set_callbacks( OpenDecoder, CloseDecoder )
+vlc_module_end ()
+
+static int jstrcmp(JNIEnv* env, jobject str, const char* str2)
+{
+    int ret;
+    jsize len = (*env)->GetStringUTFLength(env, str);
+    const char *ptr;
+    if (len != (jsize) strlen(str2))
+        return -1;
+    ptr = (*env)->GetStringUTFChars(env, str, NULL);
+    ret = memcmp(ptr, str2, len);
+    (*env)->ReleaseStringUTFChars(env, str, ptr);
+    return ret;
+}
+
+/*****************************************************************************
+ * OpenDecoder: Create the decoder instance
+ *****************************************************************************/
+static int OpenDecoder(vlc_object_t *p_this)
+{
+    decoder_t *p_dec = (decoder_t*)p_this;
+    decoder_sys_t *p_sys;
+    JNIEnv* env = NULL;
+    const char *mime = NULL;
+    int num_codecs, i;
+    jclass media_codec_list_class;
+    jclass media_codec_class;
+    jclass media_codec_info_class;
+    jclass media_format_class;
+    jclass buffer_info_class;
+    jclass object_class;
+    jmethodID get_codec_count, get_codec_info_at, is_encoder, get_supported_types, get_name;
+    jmethodID create_by_codec_name, create_video_format, set_integer, set_bytebuffer, configure, start;
+    jmethodID buffer_info_ctor;
+    jobject codec_info = NULL, codec_name = NULL;
+    jobject format;
+
+    if (p_dec->fmt_in.i_cat != VIDEO_ES && !p_dec->b_force)
+        return VLC_EGENERIC;
+
+    switch (p_dec->fmt_in.i_codec) {
+    case VLC_CODEC_H264: mime = "video/avc"; break;
+    case VLC_CODEC_H263: mime = "video/3gpp"; break;
+    case VLC_CODEC_MP4V: mime = "video/mp4v-es"; break;
+    default:
+        msg_Err(p_dec, "codec %d not supported", p_dec->fmt_in.i_codec);
+        return VLC_EGENERIC;
+    }
+    /* Allocate the memory needed to store the decoder's structure */
+    if ((p_dec->p_sys = p_sys = calloc(1, sizeof(*p_sys))) == NULL)
+        return VLC_ENOMEM;
+
+    p_dec->pf_decode_video = DecodeVideo;
+
+
+    p_dec->fmt_out.i_cat = p_dec->fmt_in.i_cat;
+    p_dec->fmt_out.video = p_dec->fmt_in.video;
+    p_dec->fmt_out.audio = p_dec->fmt_in.audio;
+    p_dec->b_need_packetized = true;
+
+    (*myVm)->AttachCurrentThread(myVm, &env, NULL);
+    media_codec_class = (*env)->FindClass(env, "android/media/MediaCodec");
+    if ((*env)->ExceptionOccurred(env)) {
+        (*env)->ExceptionClear(env);
+        goto error;
+    }
+    object_class = (*env)->FindClass(env, "java/lang/Object");
+    media_codec_list_class = (*env)->FindClass(env, "android/media/MediaCodecList");
+    media_codec_info_class = (*env)->FindClass(env, "android/media/MediaCodecInfo");
+    media_format_class = (*env)->FindClass(env, "android/media/MediaFormat");
+    buffer_info_class = (*env)->FindClass(env, "android/media/MediaCodec$BufferInfo");
+
+    p_sys->tostring = (*env)->GetMethodID(env, object_class, "toString", "()Ljava/lang/String;");
+    get_codec_count = (*env)->GetStaticMethodID(env, media_codec_list_class, "getCodecCount", "()I");
+    get_codec_info_at = (*env)->GetStaticMethodID(env, media_codec_list_class, "getCodecInfoAt", "(I)Landroid/media/MediaCodecInfo;");
+    is_encoder = (*env)->GetMethodID(env, media_codec_info_class, "isEncoder", "()Z");
+    get_supported_types = (*env)->GetMethodID(env, media_codec_info_class, "getSupportedTypes", "()[Ljava/lang/String;");
+    get_name = (*env)->GetMethodID(env, media_codec_info_class, "getName", "()Ljava/lang/String;");
+    create_by_codec_name = (*env)->GetStaticMethodID(env, media_codec_class, "createByCodecName", "(Ljava/lang/String;)Landroid/media/MediaCodec;");
+    configure = (*env)->GetMethodID(env, media_codec_class, "configure", "(Landroid/media/MediaFormat;Landroid/view/Surface;Landroid/media/MediaCrypto;I)V");
+    start = (*env)->GetMethodID(env, media_codec_class, "start", "()V");
+    p_sys->stop = (*env)->GetMethodID(env, media_codec_class, "stop", "()V");
+    p_sys->flush = (*env)->GetMethodID(env, media_codec_class, "flush", "()V");
+    p_sys->release = (*env)->GetMethodID(env, media_codec_class, "release", "()V");
+    p_sys->get_output_format = (*env)->GetMethodID(env, media_codec_class, "getOutputFormat", "()Landroid/media/MediaFormat;");
+    p_sys->get_input_buffers = (*env)->GetMethodID(env, media_codec_class, "getInputBuffers", "()[Ljava/nio/ByteBuffer;");
+    p_sys->get_output_buffers = (*env)->GetMethodID(env, media_codec_class, "getOutputBuffers", "()[Ljava/nio/ByteBuffer;");
+    p_sys->dequeue_input_buffer = (*env)->GetMethodID(env, media_codec_class, "dequeueInputBuffer", "(J)I");
+    p_sys->dequeue_output_buffer = (*env)->GetMethodID(env, media_codec_class, "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I");
+    p_sys->queue_input_buffer = (*env)->GetMethodID(env, media_codec_class, "queueInputBuffer", "(IIIJI)V");
+    p_sys->release_output_buffer = (*env)->GetMethodID(env, media_codec_class, "releaseOutputBuffer", "(IZ)V");
+    create_video_format = (*env)->GetStaticMethodID(env, media_format_class, "createVideoFormat", "(Ljava/lang/String;II)Landroid/media/MediaFormat;");
+    set_integer = (*env)->GetMethodID(env, media_format_class, "setInteger", "(Ljava/lang/String;I)V");
+    p_sys->get_integer = (*env)->GetMethodID(env, media_format_class, "getInteger", "(Ljava/lang/String;)I");
+    set_bytebuffer = (*env)->GetMethodID(env, media_format_class, "setByteBuffer", "(Ljava/lang/String;Ljava/nio/ByteBuffer;)V");
+    buffer_info_ctor = (*env)->GetMethodID(env, buffer_info_class, "<init>", "()V");
+
+    p_sys->size_field = (*env)->GetFieldID(env, buffer_info_class, "size", "I");
+    p_sys->offset_field = (*env)->GetFieldID(env, buffer_info_class, "offset", "I");
+    p_sys->pts_field = (*env)->GetFieldID(env, buffer_info_class, "presentationTimeUs", "J");
+
+    num_codecs = (*env)->CallStaticIntMethod(env, media_codec_list_class, get_codec_count);
+
+    msg_Dbg(p_dec, "%d codecs", num_codecs);
+    for (i = 0; i < num_codecs; i++) {
+        jobject info = (*env)->CallStaticObjectMethod(env, media_codec_list_class, get_codec_info_at, i);
+        jobject types;
+        int num_types, j, found = 0;
+        if ((*env)->CallBooleanMethod(env, info, is_encoder)) {
+            (*env)->DeleteLocalRef(env, info);
+            continue;
+        }
+        types = (*env)->CallObjectMethod(env, info, get_supported_types);
+        num_types = (*env)->GetArrayLength(env, types);
+        for (j = 0; j < num_types && !found; j++) {
+            jobject type = (*env)->GetObjectArrayElement(env, types, j);
+            if (!jstrcmp(env, type, mime))
+                found = 1;
+            (*env)->DeleteLocalRef(env, type);
+        }
+        if (found) {
+            jobject name = (*env)->CallObjectMethod(env, info, get_name);
+            jsize name_len = (*env)->GetStringUTFLength(env, name);
+            const char *name_ptr = (*env)->GetStringUTFChars(env, name, NULL);
+            msg_Dbg(p_dec, "using %.*s", name_len, name_ptr);
+            (*env)->ReleaseStringUTFChars(env, name, name_ptr);
+            codec_info = info;
+            codec_name = name;
+            break;
+        }
+        (*env)->DeleteLocalRef(env, info);
+    }
+
+    if (!codec_info) {
+        msg_Dbg(p_dec, "No suitable codec matching %s was found", mime);
+        goto error;
+    }
+
+    // This method doesn't handle errors nicely, it crashes if the codec isn't found
+    p_sys->codec = (*env)->CallStaticObjectMethod(env, media_codec_class, create_by_codec_name, codec_name);
+    p_sys->codec = (*env)->NewGlobalRef(env, p_sys->codec);
+
+    format = (*env)->CallStaticObjectMethod(env, media_format_class, create_video_format, (*env)->NewStringUTF(env, mime), p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height);
+
+    if (p_dec->fmt_in.i_extra) {
+        jclass bytebuffer_class = (*env)->FindClass(env, "java/nio/ByteBuffer");
+        // Allocate a byte buffer via allocateDirect in java instead of NewDirectByteBuffer, since the latter doesn't allocate storage of its own, and we don't know how long the codec uses the buffer.
+        jmethodID allocate_direct = (*env)->GetStaticMethodID(env, bytebuffer_class, "allocateDirect", "(I)Ljava/nio/ByteBuffer;");
+        jmethodID limit = (*env)->GetMethodID(env, bytebuffer_class, "limit", "(I)Ljava/nio/Buffer;");
+        int buf_size = p_dec->fmt_in.i_extra + 20;
+        jobject bytebuf = (*env)->CallStaticObjectMethod(env, bytebuffer_class, allocate_direct, buf_size);
+        uint32_t size = p_dec->fmt_in.i_extra;
+        uint8_t *ptr = (*env)->GetDirectBufferAddress(env, bytebuf);
+        if (p_dec->fmt_in.i_codec == VLC_CODEC_H264 && ((uint8_t*)p_dec->fmt_in.p_extra)[0] == 1) {
+            convert_sps_pps(p_dec, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra,
+                            ptr, buf_size,
+                            &size, &p_sys->nal_size);
+        } else {
+            memcpy(ptr, p_dec->fmt_in.p_extra, size);
+        }
+        (*env)->CallObjectMethod(env, bytebuf, limit, size);
+        (*env)->CallVoidMethod(env, format, set_bytebuffer, (*env)->NewStringUTF(env, "csd-0"), bytebuf);
+        (*env)->DeleteLocalRef(env, bytebuf);
+    }
+
+    (*env)->CallVoidMethod(env, p_sys->codec, configure, format, NULL, NULL, 0);
+    if ((*env)->ExceptionOccurred(env)) {
+        (*env)->ExceptionClear(env);
+        goto error;
+    }
+    (*env)->CallVoidMethod(env, p_sys->codec, start);
+    p_sys->started = 1;
+
+    p_sys->input_buffers = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_input_buffers);
+    p_sys->output_buffers = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_output_buffers);
+    p_sys->buffer_info = (*env)->NewObject(env, buffer_info_class, buffer_info_ctor);
+    p_sys->input_buffers = (*env)->NewGlobalRef(env, p_sys->input_buffers);
+    p_sys->output_buffers = (*env)->NewGlobalRef(env, p_sys->output_buffers);
+    p_sys->buffer_info = (*env)->NewGlobalRef(env, p_sys->buffer_info);
+    (*env)->DeleteLocalRef(env, format);
+
+/*
+    // This crashes at the moment
+    format = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_output_format);
+
+    p_dec->fmt_out.video.i_width = (*env)->CallIntMethod(env, format, get_integer, (*env)->NewStringUTF(env, "width"));
+    p_dec->fmt_out.video.i_height = (*env)->CallIntMethod(env, format, get_integer, (*env)->NewStringUTF(env, "height"));
+    p_sys->pixel_format = (*env)->CallIntMethod(env, format, get_integer, (*env)->NewStringUTF(env, "color-format"));
+    if ((*env)->ExceptionOccurred(env))
+        (*env)->ExceptionClear(env);
+    msg_Dbg(p_dec, "width %d height %d pixfmt %d", p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height, p_sys->pixel_format);
+*/
+    (*myVm)->DetachCurrentThread(myVm);
+    return VLC_SUCCESS;
+
+ error:
+    (*myVm)->DetachCurrentThread(myVm);
+    CloseDecoder(p_this);
+    return VLC_EGENERIC;
+}
+
+static void CloseDecoder(vlc_object_t *p_this)
+{
+    decoder_t *p_dec = (decoder_t *)p_this;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    JNIEnv *env = NULL;
+
+    if (!p_sys)
+        return;
+
+    (*myVm)->AttachCurrentThread(myVm, &env, NULL);
+    if (p_sys->input_buffers)
+        (*env)->DeleteGlobalRef(env, p_sys->input_buffers);
+    if (p_sys->output_buffers)
+        (*env)->DeleteGlobalRef(env, p_sys->output_buffers);
+    if (p_sys->codec) {
+        if (p_sys->started)
+            (*env)->CallVoidMethod(env, p_sys->codec, p_sys->stop);
+        (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release);
+        (*env)->DeleteGlobalRef(env, p_sys->codec);
+    }
+    if (p_sys->buffer_info)
+        (*env)->DeleteGlobalRef(env, p_sys->buffer_info);
+    (*myVm)->DetachCurrentThread(myVm);
+
+    free(p_sys);
+}
+
+static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, int loop)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    do {
+        int index = (*env)->CallIntMethod(env, p_sys->codec, p_sys->dequeue_output_buffer, p_sys->buffer_info, (jlong) 0);
+        if (index >= 0) {
+            jobject buf = (*env)->GetObjectArrayElement(env, p_sys->output_buffers, index);
+            jsize buf_size = (*env)->GetDirectBufferCapacity(env, buf);
+            uint8_t *ptr = (*env)->GetDirectBufferAddress(env, buf);
+            if (!*pp_pic)
+                *pp_pic = decoder_NewPicture(p_dec);
+            if (*pp_pic) {
+                picture_t *p_pic = *pp_pic;
+                int size = (*env)->GetIntField(env, p_sys->buffer_info, p_sys->size_field);
+                int offset = (*env)->GetIntField(env, p_sys->buffer_info, p_sys->offset_field);
+                unsigned int chroma_div;
+                ptr += offset; // Check the size parameter as well
+		// TODO: Use crop_top/crop_left as well? Or is that already taken into account? Shouldn't be done on OMX_TI_COLOR_FormatYUV420PackedSemiPlanar at least.
+                p_pic->date = (*env)->GetLongField(env, p_sys->buffer_info, p_sys->pts_field);
+                GetVlcChromaSizes(p_dec->fmt_out.i_codec, p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height, NULL, NULL, &chroma_div);
+                CopyOmxPictureInternal(p_sys->pixel_format, p_pic, p_sys->slice_height, p_sys->stride, ptr, chroma_div);
+            }
+            (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, index, false);
+            (*env)->DeleteLocalRef(env, buf);
+        } else if (index == -3) { // INFO_OUTPUT_BUFFERS_CHANGED
+            msg_Dbg(p_dec, "output buffers changed");
+            (*env)->DeleteGlobalRef(env, p_sys->output_buffers);
+            p_sys->output_buffers = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_output_buffers);
+            p_sys->output_buffers = (*env)->NewGlobalRef(env, p_sys->output_buffers);
+            continue;
+        } else if (index == -2) { // INFO_OUTPUT_FORMAT_CHANGED
+            jobject format, format_string;
+            int crop_right, crop_bottom;
+            const char *name = "unknown";
+            const char *format_ptr;
+            jsize format_len;
+            int width, height;
+
+            format = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_output_format);
+            format_string = (*env)->CallObjectMethod(env, format, p_sys->tostring);
+
+            format_len = (*env)->GetStringUTFLength(env, format_string);
+            format_ptr = (*env)->GetStringUTFChars(env, format_string, NULL);
+            msg_Dbg(p_dec, "output format changed: %.*s", format_len, format_ptr);
+            (*env)->ReleaseStringUTFChars(env, format_string, format_ptr);
+
+            width = (*env)->CallIntMethod(env, format, p_sys->get_integer, (*env)->NewStringUTF(env, "width"));
+            height = (*env)->CallIntMethod(env, format, p_sys->get_integer, (*env)->NewStringUTF(env, "height"));
+            p_sys->stride = (*env)->CallIntMethod(env, format, p_sys->get_integer, (*env)->NewStringUTF(env, "stride"));
+            p_sys->slice_height = (*env)->CallIntMethod(env, format, p_sys->get_integer, (*env)->NewStringUTF(env, "slice-height"));
+            p_sys->pixel_format = (*env)->CallIntMethod(env, format, p_sys->get_integer, (*env)->NewStringUTF(env, "color-format"));
+            p_sys->crop_left = (*env)->CallIntMethod(env, format, p_sys->get_integer, (*env)->NewStringUTF(env, "crop-left"));
+            p_sys->crop_top = (*env)->CallIntMethod(env, format, p_sys->get_integer, (*env)->NewStringUTF(env, "crop-top"));
+            crop_right = (*env)->CallIntMethod(env, format, p_sys->get_integer, (*env)->NewStringUTF(env, "crop-right"));
+            crop_bottom = (*env)->CallIntMethod(env, format, p_sys->get_integer, (*env)->NewStringUTF(env, "crop-bottom"));
+
+            GetVlcChromaFormat(p_sys->pixel_format, &p_dec->fmt_out.i_codec, &name);
+            msg_Dbg(p_dec, "output: %d %s, %dx%d stride %d %d, crop %d %d %d %d", p_sys->pixel_format, name, width, height, p_sys->stride, p_sys->slice_height, p_sys->crop_left, p_sys->crop_top, crop_right, crop_bottom);
+
+            p_dec->fmt_out.video.i_width = crop_right + 1 - p_sys->crop_left;
+            p_dec->fmt_out.video.i_height = crop_bottom + 1 - p_sys->crop_top;
+            if (p_sys->stride <= 0)
+                p_sys->stride = width;
+            if (p_sys->slice_height <= 0)
+                p_sys->slice_height = height;
+            if ((*env)->ExceptionOccurred(env))
+                (*env)->ExceptionClear(env);
+            if (p_sys->pixel_format == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar) {
+                p_sys->slice_height -= p_sys->crop_top/2;
+                /* Reset crop top/left here, since the offset parameter already includes this - not sure if this is intended on other pixel formats or not, need to verify with other pixel formats that set a nonzero top/left crop. */
+                p_sys->crop_top = 0;
+                p_sys->crop_left = 0;
+            }
+            continue;
+        } else {
+            break;
+        }
+    } while (loop);
+}
+
+static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    picture_t *p_pic = NULL;
+    block_t *p_block;
+    JNIEnv *env = NULL;
+    jlong timeout = 0;
+
+    if (!pp_block || !*pp_block)
+        return NULL;
+
+    p_block = *pp_block;
+
+    (*myVm)->AttachCurrentThread(myVm, &env, NULL);
+
+    if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) {
+        block_Release(p_block);
+        if (p_sys->decoded)
+            (*env)->CallVoidMethod(env, p_sys->codec, p_sys->flush);
+        p_sys->decoded = 0;
+        (*myVm)->DetachCurrentThread(myVm);
+        return NULL;
+    }
+
+    while (true) {
+        int index = (*env)->CallIntMethod(env, p_sys->codec, p_sys->dequeue_input_buffer, timeout);
+        jobject buf;
+        uint8_t *bufptr;
+        jsize size;
+        int64_t ts;
+        if (index < 0) {
+            GetOutput(p_dec, env, &p_pic, timeout > 0);
+            timeout = 30;
+            continue;
+        }
+        buf = (*env)->GetObjectArrayElement(env, p_sys->input_buffers, index);
+        size = (*env)->GetDirectBufferCapacity(env, buf);
+        bufptr = (*env)->GetDirectBufferAddress(env, buf);
+        if (size > p_block->i_buffer)
+            size = p_block->i_buffer;
+        memcpy(bufptr, p_block->p_buffer, size);
+
+        if (p_sys->nal_size) {
+            int len = size;
+            uint8_t *ptr = bufptr;
+            while (len >= p_sys->nal_size) {
+                uint32_t i, nal_len = 0;
+                for (i = 0; i < p_sys->nal_size; i++) {
+                    nal_len = (nal_len << 8) | ptr[i];
+                    ptr[i] = 0;
+                }
+                ptr[p_sys->nal_size - 1] = 1;
+                if (nal_len > INT_MAX || nal_len > (unsigned int) len)
+                    break;
+                ptr += nal_len + p_sys->nal_size;
+                len -= nal_len + p_sys->nal_size;
+            }
+        }
+
+        ts = p_block->i_pts;
+        if (!ts && p_block->i_dts)
+            ts = p_block->i_dts;
+        (*env)->CallVoidMethod(env, p_sys->codec, p_sys->queue_input_buffer, index, 0, size, ts, 0);
+        (*env)->DeleteLocalRef(env, buf);
+        p_sys->decoded = 1;
+        break;
+    }
+    if (!p_pic)
+        GetOutput(p_dec, env, &p_pic, 0);
+    (*myVm)->DetachCurrentThread(myVm);
+
+    block_Release(p_block);
+    *pp_block = NULL;
+
+    return p_pic;
+}
-- 
1.7.10




More information about the vlc-devel mailing list