[vlc-devel] [PATCH] mediacodec: ndk: fix undefined behavior with Hi10P profile

Thomas Guillem thomas at gllm.fr
Mon Jun 1 16:57:52 CEST 2015


Even the latest android version (Nexus 10 5.1.1) have an undefined behavior
when playing a H264 Hi10P video

As there is no public API in NDK to get the MediaCodecList in order to list the
supported profiles, use the API from JNI.
---
 modules/codec/omxil/mediacodec.c     | 22 ++++++++--
 modules/codec/omxil/mediacodec.h     |  8 ++--
 modules/codec/omxil/mediacodec_jni.c | 79 ++++++++++++++++++------------------
 modules/codec/omxil/mediacodec_ndk.c | 18 ++++----
 4 files changed, 70 insertions(+), 57 deletions(-)

diff --git a/modules/codec/omxil/mediacodec.c b/modules/codec/omxil/mediacodec.c
index ac03db7..6853e60 100644
--- a/modules/codec/omxil/mediacodec.c
+++ b/modules/codec/omxil/mediacodec.c
@@ -136,6 +136,7 @@ struct csd
 struct decoder_sys_t
 {
     mc_api *api;
+    char *psz_name;
     uint32_t nal_size;
 
     int pixel_format;
@@ -338,6 +339,7 @@ static int StartMediaCodec(decoder_t *p_dec)
     decoder_sys_t *p_sys = p_dec->p_sys;
     int i_angle = 0, i_ret;
     size_t h264_profile = 0;
+    char *psz_name = NULL;
     jobject jsurface = NULL;
 
     if (p_dec->fmt_in.i_extra && !p_sys->p_csd)
@@ -417,21 +419,30 @@ static int StartMediaCodec(decoder_t *p_dec)
     if (p_dec->fmt_in.i_codec == VLC_CODEC_H264)
         h264_get_profile_level(&p_dec->fmt_in, &h264_profile, NULL, NULL);
 
+    psz_name = MediaCodec_GetName(VLC_OBJECT(p_dec), p_sys->mime, h264_profile);
+    if (!psz_name)
+        return VLC_EGENERIC;
+
     if (var_InheritBool(p_dec, CFG_PREFIX "dr"))
         jsurface = jni_LockAndGetAndroidJavaSurface();
-    i_ret = p_sys->api->start(p_sys->api, jsurface, p_sys->mime, p_sys->i_width,
-                              p_sys->i_height, h264_profile, i_angle);
+    i_ret = p_sys->api->start(p_sys->api, jsurface, psz_name, p_sys->mime,
+                              p_sys->i_width, p_sys->i_height, i_angle);
     if (jsurface)
         jni_UnlockAndroidSurface();
 
     if (i_ret == VLC_SUCCESS)
     {
+        p_sys->psz_name = psz_name;
         if (p_sys->api->b_direct_rendering)
             p_dec->fmt_out.i_codec = VLC_CODEC_ANDROID_OPAQUE;
         p_sys->b_update_format = true;
         return VLC_SUCCESS;
-    } else
+    }
+    else
+    {
+        free(psz_name);
         return VLC_EGENERIC;
+    }
 }
 
 /*****************************************************************************
@@ -446,6 +457,9 @@ static void StopMediaCodec(decoder_t *p_dec)
     if (p_sys->api->b_direct_rendering)
         InvalidateAllPictures(p_dec);
 
+    free(p_sys->psz_name);
+    p_sys->psz_name = NULL;
+
     p_sys->api->stop(p_sys->api);
 }
 
@@ -798,7 +812,7 @@ static int GetOutput(decoder_t *p_dec, picture_t *p_pic,
                                       p_sys->stride, &p_sys->architecture_specific_data);
         if (p_sys->pixel_format == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar)
             p_sys->slice_height -= out.u.conf.crop_top/2;
-        if (IgnoreOmxDecoderPadding(p_sys->api->psz_name)) {
+        if (IgnoreOmxDecoderPadding(p_sys->psz_name)) {
             p_sys->slice_height = 0;
             p_sys->stride = p_dec->fmt_out.video.i_width;
         }
diff --git a/modules/codec/omxil/mediacodec.h b/modules/codec/omxil/mediacodec.h
index 551f4f0..90d7676 100644
--- a/modules/codec/omxil/mediacodec.h
+++ b/modules/codec/omxil/mediacodec.h
@@ -29,6 +29,8 @@ typedef struct mc_api_out mc_api_out;
 
 typedef int (*pf_MediaCodecApi_init)(mc_api*);
 
+char* MediaCodec_GetName(vlc_object_t *p_obj, const char *psz_mime,
+                         size_t h264_profile);
 int MediaCodecJni_Init(mc_api*);
 int MediaCodecNdk_Init(mc_api*);
 
@@ -67,15 +69,13 @@ struct mc_api
 
     mc_api_sys *p_sys;
 
-    const char *psz_name;
     bool b_started;
     bool b_direct_rendering;
     bool b_support_interlaced;
 
     void (*clean)(mc_api *);
-    int (*start)(mc_api *, jobject jsurface, const char *psz_mime,
-                 int i_width, int i_height,
-                 size_t h264_profile, int i_angle);
+    int (*start)(mc_api *, jobject jsurface, const char *psz_name,
+                 const char *psz_mime, int i_width, int i_height, int i_angle);
     int (*stop)(mc_api *);
     int (*flush)(mc_api *);
     int (*put_in)(mc_api *, const void *p_buf, size_t i_size,
diff --git a/modules/codec/omxil/mediacodec_jni.c b/modules/codec/omxil/mediacodec_jni.c
index 432a69f..38c5f91 100644
--- a/modules/codec/omxil/mediacodec_jni.c
+++ b/modules/codec/omxil/mediacodec_jni.c
@@ -179,7 +179,7 @@ static inline bool check_exception(JNIEnv *env)
 /* Initialize all jni fields.
  * Done only one time during the first initialisation */
 static bool
-InitJNIFields (mc_api *api, JNIEnv *env)
+InitJNIFields (vlc_object_t *p_obj, JNIEnv *env)
 {
     static vlc_mutex_t lock = VLC_STATIC_MUTEX;
     static int i_init_state = -1;
@@ -197,7 +197,7 @@ InitJNIFields (mc_api *api, JNIEnv *env)
         jclass clazz = (*env)->FindClass(env, classes[i].name);
         if (CHECK_EXCEPTION())
         {
-            msg_Warn(api->p_obj, "Unable to find class %s", classes[i].name);
+            msg_Warn(p_obj, "Unable to find class %s", classes[i].name);
             goto end;
         }
         *(jclass*)((uint8_t*)&jfields + classes[i].offset) =
@@ -212,7 +212,7 @@ InitJNIFields (mc_api *api, JNIEnv *env)
 
         if (CHECK_EXCEPTION())
         {
-            msg_Warn(api->p_obj, "Unable to find class %s", members[i].class);
+            msg_Warn(p_obj, "Unable to find class %s", members[i].class);
             goto end;
         }
 
@@ -232,7 +232,7 @@ InitJNIFields (mc_api *api, JNIEnv *env)
         }
         if (CHECK_EXCEPTION())
         {
-            msg_Warn(api->p_obj, "Unable to find the member %s in %s",
+            msg_Warn(p_obj, "Unable to find the member %s in %s",
                      members[i].name, members[i].class);
             if (members[i].critical)
                 goto end;
@@ -247,7 +247,7 @@ InitJNIFields (mc_api *api, JNIEnv *env)
     }
     else if (!jfields.get_output_buffers && !jfields.get_input_buffers)
     {
-        msg_Err(api->p_obj, "Unable to find get Output/Input Buffer/Buffers");
+        msg_Err(p_obj, "Unable to find get Output/Input Buffer/Buffers");
         goto end;
     }
 
@@ -255,7 +255,7 @@ InitJNIFields (mc_api *api, JNIEnv *env)
 end:
     ret = i_init_state == 1;
     if( !ret )
-        msg_Err( api->p_obj, "MediaCodec jni init failed" );
+        msg_Err(p_obj, "MediaCodec jni init failed");
 
     vlc_mutex_unlock( &lock );
     return ret;
@@ -270,20 +270,28 @@ struct mc_api_sys
     jobject codec;
     jobject buffer_info;
     jobject input_buffers, output_buffers;
-
-    char *psz_name;
 };
 
 /*****************************************************************************
- * GetMediaCodecName
+ * MediaCodec_GetName
  *****************************************************************************/
-static jstring GetMediaCodecName(mc_api *api, JNIEnv *env,
-                                 const char *psz_mime, jstring jmime,
-                                 size_t h264_profile)
+char* MediaCodec_GetName(vlc_object_t *p_obj, const char *psz_mime,
+                         size_t h264_profile)
 {
-    mc_api_sys *p_sys = api->p_sys;
+    JNIEnv *env;
     int num_codecs;
-    jstring jcodec_name = NULL;
+    jstring jmime;
+    char *psz_name = NULL;
+
+    if (!(env = jni_get_env(THREAD_NAME)))
+        return NULL;
+
+    if (!InitJNIFields(p_obj, env))
+        return NULL;
+
+    jmime = (*env)->NewStringUTF(env, psz_mime);
+    if (!jmime)
+        return NULL;
 
     num_codecs = (*env)->CallStaticIntMethod(env,
                                              jfields.media_codec_list_class,
@@ -319,7 +327,7 @@ static jstring GetMediaCodecName(mc_api *api, JNIEnv *env,
                                                       jmime);
         if (CHECK_EXCEPTION())
         {
-            msg_Warn(api->p_obj, "Exception occurred in MediaCodecInfo.getCapabilitiesForType");
+            msg_Warn(p_obj, "Exception occurred in MediaCodecInfo.getCapabilitiesForType");
             goto loopclean;
         }
         else if (codec_capabilities)
@@ -328,7 +336,7 @@ static jstring GetMediaCodecName(mc_api *api, JNIEnv *env,
             if (profile_levels)
                 profile_levels_len = (*env)->GetArrayLength(env, profile_levels);
         }
-        msg_Dbg(api->p_obj, "Number of profile levels: %d", profile_levels_len);
+        msg_Dbg(p_obj, "Number of profile levels: %d", profile_levels_len);
 
         types = (*env)->CallObjectMethod(env, info, jfields.get_supported_types);
         num_types = (*env)->GetArrayLength(env, types);
@@ -370,18 +378,19 @@ static jstring GetMediaCodecName(mc_api *api, JNIEnv *env,
         }
         if (found)
         {
-            msg_Dbg(api->p_obj, "using %.*s", name_len, name_ptr);
-            p_sys->psz_name = malloc(name_len + 1);
-            memcpy(p_sys->psz_name, name_ptr, name_len);
-            p_sys->psz_name[name_len] = '\0';
-            jcodec_name = name;
+            msg_Dbg(p_obj, "using %.*s", name_len, name_ptr);
+            psz_name = malloc(name_len + 1);
+            if (psz_name)
+            {
+                memcpy(psz_name, name_ptr, name_len);
+                psz_name[name_len] = '\0';
+            }
         }
 loopclean:
         if (name)
         {
             (*env)->ReleaseStringUTFChars(env, name, name_ptr);
-            if (jcodec_name != name)
-                (*env)->DeleteLocalRef(env, name);
+            (*env)->DeleteLocalRef(env, name);
         }
         if (profile_levels)
             (*env)->DeleteLocalRef(env, profile_levels);
@@ -394,7 +403,9 @@ loopclean:
         if (found)
             break;
     }
-    return jcodec_name;
+    (*env)->DeleteLocalRef(env, jmime);
+
+    return psz_name;
 }
 
 /*****************************************************************************
@@ -407,9 +418,6 @@ static int Stop(mc_api *api)
 
     api->b_direct_rendering = false;
     api->b_support_interlaced = false;
-    api->psz_name = NULL;
-
-    free(p_sys->psz_name);
 
     GET_ENV();
 
@@ -451,8 +459,8 @@ static int Stop(mc_api *api)
 /*****************************************************************************
  * Start
  *****************************************************************************/
-static int Start(mc_api *api, jobject jsurface, const char *psz_mime,
-                 int i_width, int i_height, size_t h264_profile, int i_angle)
+static int Start(mc_api *api, jobject jsurface, const char *psz_name,
+                 const char *psz_mime, int i_width, int i_height, int i_angle)
 {
     mc_api_sys *p_sys = api->p_sys;
     JNIEnv* env = NULL;
@@ -470,15 +478,9 @@ static int Start(mc_api *api, jobject jsurface, const char *psz_mime,
     GET_ENV();
 
     jmime = (*env)->NewStringUTF(env, psz_mime);
-    if (!jmime)
-        return VLC_EGENERIC;
-
-    jcodec_name = GetMediaCodecName(api, env, psz_mime, jmime, h264_profile);
-    if (!jcodec_name)
-    {
-        msg_Dbg(api->p_obj, "No suitable codec matching %s was found", psz_mime);
+    jcodec_name = (*env)->NewStringUTF(env, psz_name);
+    if (!jmime || !jcodec_name)
         goto error;
-    }
 
     /* This method doesn't handle errors nicely, it crashes if the codec isn't
      * found.  (The same goes for createDecoderByType.) This is fixed in latest
@@ -573,7 +575,6 @@ static int Start(mc_api *api, jobject jsurface, const char *psz_mime,
     /* Allow interlaced picture only after API 21 */
     api->b_direct_rendering = b_direct_rendering;
     api->b_support_interlaced = jfields.get_input_buffer && jfields.get_output_buffer;
-    api->psz_name = p_sys->psz_name;
     i_ret = VLC_SUCCESS;
     msg_Dbg(api->p_obj, "MediaCodec via JNI opened");
 
@@ -828,7 +829,7 @@ int MediaCodecJni_Init(mc_api *api)
 
     GET_ENV();
 
-    if (!InitJNIFields(api, env))
+    if (!InitJNIFields(api->p_obj, env))
         return VLC_EGENERIC;
 
     api->p_sys = calloc(1, sizeof(mc_api_sys));
diff --git a/modules/codec/omxil/mediacodec_ndk.c b/modules/codec/omxil/mediacodec_ndk.c
index 9eec32d..a16c657 100644
--- a/modules/codec/omxil/mediacodec_ndk.c
+++ b/modules/codec/omxil/mediacodec_ndk.c
@@ -106,7 +106,7 @@ typedef struct AMediaCrypto AMediaCrypto;
  * Ndk symbols
  *****************************************************************************/
 
-typedef AMediaCodec* (*pf_AMediaCodec_createDecoderByType)(const char *mime_type);
+typedef AMediaCodec* (*pf_AMediaCodec_createCodecByName)(const char *name);
 
 typedef media_status_t (*pf_AMediaCodec_configure)(AMediaCodec*,
         const AMediaFormat* format,
@@ -157,7 +157,7 @@ typedef bool (*pf_AMediaFormat_getInt32)(AMediaFormat*,
 struct syms
 {
     struct {
-        pf_AMediaCodec_createDecoderByType createDecoderByType;
+        pf_AMediaCodec_createCodecByName createCodecByName;
         pf_AMediaCodec_configure configure;
         pf_AMediaCodec_start start;
         pf_AMediaCodec_stop stop;
@@ -191,7 +191,7 @@ struct members
 static struct members members[] =
 {
 #define OFF(x) offsetof(struct syms, AMediaCodec.x)
-    { "AMediaCodec_createDecoderByType", OFF(createDecoderByType), true },
+    { "AMediaCodec_createCodecByName", OFF(createCodecByName), true },
     { "AMediaCodec_configure", OFF(configure), true },
     { "AMediaCodec_start", OFF(start), true },
     { "AMediaCodec_stop", OFF(stop), true },
@@ -283,7 +283,6 @@ static int Stop(mc_api *api)
 
     api->b_direct_rendering = false;
     api->b_support_interlaced = false;
-    api->psz_name = NULL;
 
     if (p_sys->p_codec)
     {
@@ -313,17 +312,16 @@ static int Stop(mc_api *api)
 /*****************************************************************************
  * Start
  *****************************************************************************/
-static int Start(mc_api *api, jobject jsurface, const char *psz_mime,
-                 int i_width, int i_height, size_t h264_profile, int i_angle)
+static int Start(mc_api *api, jobject jsurface, const char *psz_name,
+                 const char *psz_mime, int i_width, int i_height, int i_angle)
 {
     mc_api_sys *p_sys = api->p_sys;
     int i_ret = VLC_EGENERIC;
-    (void) h264_profile;
 
-    p_sys->p_codec = syms.AMediaCodec.createDecoderByType(psz_mime);
+    p_sys->p_codec = syms.AMediaCodec.createCodecByName(psz_name);
     if (!p_sys->p_codec)
     {
-        msg_Err(api->p_obj, "AMediaCodec.createDecoderByType for %s failed", psz_mime);
+        msg_Err(api->p_obj, "AMediaCodec.createCodecByName for %s failed", psz_name);
         goto error;
     }
 
@@ -338,6 +336,7 @@ static int Start(mc_api *api, jobject jsurface, const char *psz_mime,
     syms.AMediaFormat.setInt32(p_sys->p_format, "width", i_width);
     syms.AMediaFormat.setInt32(p_sys->p_format, "height", i_height);
     syms.AMediaFormat.setInt32(p_sys->p_format, "rotation-degrees", i_angle);
+    syms.AMediaFormat.setInt32(p_sys->p_format, "encoder", 0);
 
     if (jsurface)
     {
@@ -363,7 +362,6 @@ static int Start(mc_api *api, jobject jsurface, const char *psz_mime,
     api->b_started = true;
     api->b_direct_rendering = !!p_sys->p_anw;
     api->b_support_interlaced = true;
-    api->psz_name = ""; // TODO
     i_ret = VLC_SUCCESS;
 
     msg_Dbg(api->p_obj, "MediaCodec via NDK opened");
-- 
2.1.4




More information about the vlc-devel mailing list