amc: Create JNI wrapper for MediaCodecList
authorXavier Claessens <xavier.claessens@collabora.com>
Sun, 11 Nov 2018 13:51:04 +0000 (08:51 -0500)
committerXavier Claessens <xavier.claessens@collabora.com>
Tue, 20 Aug 2019 12:53:55 +0000 (08:53 -0400)
There is no NdkMediaCodecList API yet, but it is still better to isolate
JNI code. This will facilitate porting to a native API if Google ever
release one.

sys/androidmedia/gstamc-codeclist.h [new file with mode: 0644]
sys/androidmedia/gstamc.c
sys/androidmedia/gstamc.h
sys/androidmedia/jni/gstamc-codeclist-jni.c [new file with mode: 0644]
sys/androidmedia/meson.build

diff --git a/sys/androidmedia/gstamc-codeclist.h b/sys/androidmedia/gstamc-codeclist.h
new file mode 100644 (file)
index 0000000..f7ebb01
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012,2018 Collabora Ltd.
+ *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * This library 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
+ * version 2.1 of the License.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#ifndef __GST_AMC_CODECLIST_H__
+#define __GST_AMC_CODECLIST_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstAmcCodecInfoHandle GstAmcCodecInfoHandle;
+typedef struct _GstAmcCodecCapabilitiesHandle GstAmcCodecCapabilitiesHandle;
+typedef struct _GstAmcCodecProfileLevel GstAmcCodecProfileLevel;
+
+struct _GstAmcCodecProfileLevel
+{
+  gint profile;
+  gint level;
+};
+
+gboolean gst_amc_codeclist_static_init (void);
+
+gboolean gst_amc_codeclist_get_count (gint * count, GError **err);
+GstAmcCodecInfoHandle * gst_amc_codeclist_get_codec_info_at (gint index,
+    GError **err);
+
+void gst_amc_codec_info_handle_free (GstAmcCodecInfoHandle * handle);
+gchar * gst_amc_codec_info_handle_get_name (GstAmcCodecInfoHandle * handle,
+    GError ** err);
+gboolean gst_amc_codec_info_handle_is_encoder (GstAmcCodecInfoHandle * handle,
+    gboolean * is_encoder, GError ** err);
+gchar ** gst_amc_codec_info_handle_get_supported_types (
+    GstAmcCodecInfoHandle * handle, gsize * length, GError ** err);
+GstAmcCodecCapabilitiesHandle * gst_amc_codec_info_handle_get_capabilities_for_type (
+    GstAmcCodecInfoHandle * handle, const gchar * type, GError ** err);
+
+void gst_amc_codec_capabilities_handle_free (
+    GstAmcCodecCapabilitiesHandle * handle);
+gint * gst_amc_codec_capabilities_handle_get_color_formats (
+    GstAmcCodecCapabilitiesHandle * handle, gsize * length, GError ** err);
+GstAmcCodecProfileLevel * gst_amc_codec_capabilities_handle_get_profile_levels (
+    GstAmcCodecCapabilitiesHandle * handle, gsize * length, GError ** err);
+
+G_END_DECLS
+
+#endif /* __GST_AMC_CODECLIST_H__ */
index fbf50d614a52e32f1e48c8449cd531b2b1136804..1ea4a820727c5e726254e256846170652344d042 100644 (file)
@@ -64,11 +64,9 @@ static gboolean
 scan_codecs (GstPlugin * plugin)
 {
   gboolean ret = TRUE;
-  JNIEnv *env;
-  jclass codec_list_class = NULL;
-  jmethodID get_codec_count_id, get_codec_info_at_id;
-  jint codec_count, i;
+  gint codec_count, i;
   const GstStructure *cache_data;
+  GError *error = NULL;
 
   GST_DEBUG ("Scanning codecs");
 
@@ -149,41 +147,9 @@ scan_codecs (GstPlugin * plugin)
     return TRUE;
   }
 
-  env = gst_amc_jni_get_env ();
-
-  codec_list_class = (*env)->FindClass (env, "android/media/MediaCodecList");
-  if (!codec_list_class) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get codec list class");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-
-  get_codec_count_id =
-      (*env)->GetStaticMethodID (env, codec_list_class, "getCodecCount", "()I");
-  get_codec_info_at_id =
-      (*env)->GetStaticMethodID (env, codec_list_class, "getCodecInfoAt",
-      "(I)Landroid/media/MediaCodecInfo;");
-  if (!get_codec_count_id || !get_codec_info_at_id) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get codec list method IDs");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-
-  codec_count =
-      (*env)->CallStaticIntMethod (env, codec_list_class, get_codec_count_id);
-  if ((*env)->ExceptionCheck (env)) {
-    ret = FALSE;
+  if (!gst_amc_codeclist_get_count (&codec_count, &error)) {
     GST_ERROR ("Failed to get number of available codecs");
-    (*env)->ExceptionDescribe (env);
-    (*env)->ExceptionClear (env);
+    ret = FALSE;
     goto done;
   }
 
@@ -191,79 +157,26 @@ scan_codecs (GstPlugin * plugin)
 
   for (i = 0; i < codec_count; i++) {
     GstAmcCodecInfo *gst_codec_info;
-    jobject codec_info = NULL;
-    jclass codec_info_class = NULL;
-    jmethodID get_capabilities_for_type_id, get_name_id;
-    jmethodID get_supported_types_id, is_encoder_id;
-    jobject name = NULL;
-    const gchar *name_str = NULL;
-    jboolean is_encoder;
-    jarray supported_types = NULL;
-    jsize n_supported_types;
-    jsize j;
+    GstAmcCodecInfoHandle *codec_info = NULL;
+    gchar *name_str = NULL;
+    gboolean is_encoder;
+    gchar **supported_types = NULL;
+    gsize n_supported_types;
+    gsize j;
     gboolean valid_codec = TRUE;
 
     gst_codec_info = g_new0 (GstAmcCodecInfo, 1);
 
-    codec_info =
-        (*env)->CallStaticObjectMethod (env, codec_list_class,
-        get_codec_info_at_id, i);
-    if ((*env)->ExceptionCheck (env) || !codec_info) {
+    codec_info = gst_amc_codeclist_get_codec_info_at (i, &error);
+    if (!codec_info) {
       GST_ERROR ("Failed to get codec info %d", i);
-      if ((*env)->ExceptionCheck (env)) {
-        (*env)->ExceptionDescribe (env);
-        (*env)->ExceptionClear (env);
-      }
-      valid_codec = FALSE;
-      goto next_codec;
-    }
-
-    codec_info_class = (*env)->GetObjectClass (env, codec_info);
-    if (!codec_list_class) {
-      GST_ERROR ("Failed to get codec info class");
-      if ((*env)->ExceptionCheck (env)) {
-        (*env)->ExceptionDescribe (env);
-        (*env)->ExceptionClear (env);
-      }
       valid_codec = FALSE;
       goto next_codec;
     }
 
-    get_capabilities_for_type_id =
-        (*env)->GetMethodID (env, codec_info_class, "getCapabilitiesForType",
-        "(Ljava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;");
-    get_name_id =
-        (*env)->GetMethodID (env, codec_info_class, "getName",
-        "()Ljava/lang/String;");
-    get_supported_types_id =
-        (*env)->GetMethodID (env, codec_info_class, "getSupportedTypes",
-        "()[Ljava/lang/String;");
-    is_encoder_id =
-        (*env)->GetMethodID (env, codec_info_class, "isEncoder", "()Z");
-    if (!get_capabilities_for_type_id || !get_name_id
-        || !get_supported_types_id || !is_encoder_id) {
-      GST_ERROR ("Failed to get codec info method IDs");
-      if ((*env)->ExceptionCheck (env)) {
-        (*env)->ExceptionDescribe (env);
-        (*env)->ExceptionClear (env);
-      }
-      valid_codec = FALSE;
-      goto next_codec;
-    }
-
-    name = (*env)->CallObjectMethod (env, codec_info, get_name_id);
-    if ((*env)->ExceptionCheck (env)) {
+    name_str = gst_amc_codec_info_handle_get_name (codec_info, &error);
+    if (!name_str) {
       GST_ERROR ("Failed to get codec name");
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-      valid_codec = FALSE;
-      goto next_codec;
-    }
-    name_str = (*env)->GetStringUTFChars (env, name, NULL);
-    if ((*env)->ExceptionCheck (env)) {
-      GST_ERROR ("Failed to convert codec name to UTF8");
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
       valid_codec = FALSE;
       goto next_codec;
     }
@@ -315,11 +228,8 @@ scan_codecs (GstPlugin * plugin)
     }
     gst_codec_info->name = g_strdup (name_str);
 
-    is_encoder = (*env)->CallBooleanMethod (env, codec_info, is_encoder_id);
-    if ((*env)->ExceptionCheck (env)) {
+    if (!gst_amc_codec_info_handle_is_encoder (codec_info, &is_encoder, &error)) {
       GST_ERROR ("Failed to detect if codec is an encoder");
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
       valid_codec = FALSE;
       goto next_codec;
     }
@@ -327,25 +237,16 @@ scan_codecs (GstPlugin * plugin)
     gst_codec_info->gl_output_only = FALSE;
 
     supported_types =
-        (*env)->CallObjectMethod (env, codec_info, get_supported_types_id);
-    if ((*env)->ExceptionCheck (env)) {
+        gst_amc_codec_info_handle_get_supported_types (codec_info,
+        &n_supported_types, &error);
+    if (!supported_types) {
       GST_ERROR ("Failed to get supported types");
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
       valid_codec = FALSE;
       goto next_codec;
     }
 
-    n_supported_types = (*env)->GetArrayLength (env, supported_types);
-    if ((*env)->ExceptionCheck (env)) {
-      GST_ERROR ("Failed to get supported types array length");
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-      valid_codec = FALSE;
-      goto next_codec;
-    }
-
-    GST_INFO ("Codec '%s' has %d supported types", name_str, n_supported_types);
+    GST_INFO ("Codec '%s' has %" G_GSIZE_FORMAT " supported types", name_str,
+        n_supported_types);
 
     gst_codec_info->supported_types =
         g_new0 (GstAmcCodecType, n_supported_types);
@@ -359,110 +260,41 @@ scan_codecs (GstPlugin * plugin)
 
     for (j = 0; j < n_supported_types; j++) {
       GstAmcCodecType *gst_codec_type;
-      jobject supported_type = NULL;
-      const gchar *supported_type_str = NULL;
-      jobject capabilities = NULL;
-      jclass capabilities_class = NULL;
-      jfieldID profile_levels_id, color_formats_id;
-      jobject profile_levels = NULL;
-      jobject color_formats = NULL;
-      jint *color_formats_elems = NULL;
-      jsize n_elems, k;
+      const gchar *supported_type_str;
+      GstAmcCodecCapabilitiesHandle *capabilities = NULL;
+      gint k;
 
       gst_codec_type = &gst_codec_info->supported_types[j];
-
-      supported_type = (*env)->GetObjectArrayElement (env, supported_types, j);
-      if ((*env)->ExceptionCheck (env)) {
-        GST_ERROR ("Failed to get %d-th supported type", j);
-        (*env)->ExceptionDescribe (env);
-        (*env)->ExceptionClear (env);
-        valid_codec = FALSE;
-        goto next_supported_type;
-      }
-
-      supported_type_str =
-          (*env)->GetStringUTFChars (env, supported_type, NULL);
-      if ((*env)->ExceptionCheck (env) || !supported_type_str) {
-        GST_ERROR ("Failed to convert supported type to UTF8");
-        (*env)->ExceptionDescribe (env);
-        (*env)->ExceptionClear (env);
-        valid_codec = FALSE;
-        goto next_supported_type;
-      }
+      supported_type_str = supported_types[j];
 
       GST_INFO ("Supported type '%s'", supported_type_str);
       gst_codec_type->mime = g_strdup (supported_type_str);
 
       capabilities =
-          (*env)->CallObjectMethod (env, codec_info,
-          get_capabilities_for_type_id, supported_type);
-      if ((*env)->ExceptionCheck (env)) {
+          gst_amc_codec_info_handle_get_capabilities_for_type (codec_info,
+          supported_type_str, &error);
+      if (!capabilities) {
         GST_ERROR ("Failed to get capabilities for supported type");
-        (*env)->ExceptionDescribe (env);
-        (*env)->ExceptionClear (env);
-        valid_codec = FALSE;
-        goto next_supported_type;
-      }
-
-      capabilities_class = (*env)->GetObjectClass (env, capabilities);
-      if (!capabilities_class) {
-        GST_ERROR ("Failed to get capabilities class");
-        (*env)->ExceptionDescribe (env);
-        (*env)->ExceptionClear (env);
-        valid_codec = FALSE;
-        goto next_supported_type;
-      }
-
-      color_formats_id =
-          (*env)->GetFieldID (env, capabilities_class, "colorFormats", "[I");
-      profile_levels_id =
-          (*env)->GetFieldID (env, capabilities_class, "profileLevels",
-          "[Landroid/media/MediaCodecInfo$CodecProfileLevel;");
-      if (!color_formats_id || !profile_levels_id) {
-        GST_ERROR ("Failed to get capabilities field IDs");
-        (*env)->ExceptionDescribe (env);
-        (*env)->ExceptionClear (env);
         valid_codec = FALSE;
         goto next_supported_type;
       }
 
       if (g_str_has_prefix (gst_codec_type->mime, "video/")) {
-        color_formats =
-            (*env)->GetObjectField (env, capabilities, color_formats_id);
-        if ((*env)->ExceptionCheck (env)) {
-          GST_ERROR ("Failed to get color formats");
-          (*env)->ExceptionDescribe (env);
-          (*env)->ExceptionClear (env);
-          valid_codec = FALSE;
-          goto next_supported_type;
-        }
-
-        n_elems = (*env)->GetArrayLength (env, color_formats);
-        if ((*env)->ExceptionCheck (env)) {
-          GST_ERROR ("Failed to get color formats array length");
-          (*env)->ExceptionDescribe (env);
-          (*env)->ExceptionClear (env);
-          valid_codec = FALSE;
-          goto next_supported_type;
-        }
-        gst_codec_type->n_color_formats = n_elems;
-        gst_codec_type->color_formats = g_new0 (gint, n_elems);
-        color_formats_elems =
-            (*env)->GetIntArrayElements (env, color_formats, NULL);
-        if ((*env)->ExceptionCheck (env)) {
+        gst_codec_type->color_formats =
+            gst_amc_codec_capabilities_handle_get_color_formats (capabilities,
+            &gst_codec_type->n_color_formats, &error);
+        if (!gst_codec_type->color_formats) {
           GST_ERROR ("Failed to get color format elements");
-          (*env)->ExceptionDescribe (env);
-          (*env)->ExceptionClear (env);
           valid_codec = FALSE;
           goto next_supported_type;
         }
 
-        for (k = 0; k < n_elems; k++) {
-          GST_INFO ("Color format %d: 0x%x", k, color_formats_elems[k]);
-          gst_codec_type->color_formats[k] = color_formats_elems[k];
+        for (k = 0; k < gst_codec_type->n_color_formats; k++) {
+          GST_INFO ("Color format %d: 0x%x", k,
+              gst_codec_type->color_formats[k]);
         }
 
-        if (!n_elems) {
+        if (!gst_codec_type->n_color_formats) {
           GST_ERROR ("No supported color formats for video codec");
           valid_codec = FALSE;
           goto next_supported_type;
@@ -478,118 +310,27 @@ scan_codecs (GstPlugin * plugin)
         }
       }
 
-      profile_levels =
-          (*env)->GetObjectField (env, capabilities, profile_levels_id);
-      if ((*env)->ExceptionCheck (env)) {
+      gst_codec_type->profile_levels =
+          gst_amc_codec_capabilities_handle_get_profile_levels (capabilities,
+          &gst_codec_type->n_profile_levels, &error);
+      if (!gst_codec_type->profile_levels) {
         GST_ERROR ("Failed to get profile/levels");
-        (*env)->ExceptionDescribe (env);
-        (*env)->ExceptionClear (env);
         valid_codec = FALSE;
         goto next_supported_type;
       }
 
-      n_elems = (*env)->GetArrayLength (env, profile_levels);
-      if ((*env)->ExceptionCheck (env)) {
-        GST_ERROR ("Failed to get profile/levels array length");
-        (*env)->ExceptionDescribe (env);
-        (*env)->ExceptionClear (env);
-        valid_codec = FALSE;
-        goto next_supported_type;
-      }
-      gst_codec_type->n_profile_levels = n_elems;
-      gst_codec_type->profile_levels =
-          g_malloc0 (sizeof (gst_codec_type->profile_levels[0]) * n_elems);
-      for (k = 0; k < n_elems; k++) {
-        jobject profile_level = NULL;
-        jclass profile_level_class = NULL;
-        jfieldID level_id, profile_id;
-        jint level, profile;
-
-        profile_level = (*env)->GetObjectArrayElement (env, profile_levels, k);
-        if ((*env)->ExceptionCheck (env)) {
-          GST_ERROR ("Failed to get %d-th profile/level", k);
-          (*env)->ExceptionDescribe (env);
-          (*env)->ExceptionClear (env);
-          valid_codec = FALSE;
-          goto next_profile_level;
-        }
-
-        profile_level_class = (*env)->GetObjectClass (env, profile_level);
-        if (!profile_level_class) {
-          GST_ERROR ("Failed to get profile/level class");
-          (*env)->ExceptionDescribe (env);
-          (*env)->ExceptionClear (env);
-          valid_codec = FALSE;
-          goto next_profile_level;
-        }
-
-        level_id = (*env)->GetFieldID (env, profile_level_class, "level", "I");
-        profile_id =
-            (*env)->GetFieldID (env, profile_level_class, "profile", "I");
-        if (!level_id || !profile_id) {
-          GST_ERROR ("Failed to get profile/level field IDs");
-          (*env)->ExceptionDescribe (env);
-          (*env)->ExceptionClear (env);
-          valid_codec = FALSE;
-          goto next_profile_level;
-        }
-
-        level = (*env)->GetIntField (env, profile_level, level_id);
-        if ((*env)->ExceptionCheck (env)) {
-          GST_ERROR ("Failed to get level");
-          (*env)->ExceptionDescribe (env);
-          (*env)->ExceptionClear (env);
-          valid_codec = FALSE;
-          goto next_profile_level;
-        }
-        GST_INFO ("Level %d: 0x%08x", k, level);
-        gst_codec_type->profile_levels[k].level = level;
-
-        profile = (*env)->GetIntField (env, profile_level, profile_id);
-        if ((*env)->ExceptionCheck (env)) {
-          GST_ERROR ("Failed to get profile");
-          (*env)->ExceptionDescribe (env);
-          (*env)->ExceptionClear (env);
-          valid_codec = FALSE;
-          goto next_profile_level;
-        }
-        GST_INFO ("Profile %d: 0x%08x", k, profile);
-        gst_codec_type->profile_levels[k].profile = profile;
-
-      next_profile_level:
-        if (profile_level)
-          (*env)->DeleteLocalRef (env, profile_level);
-        profile_level = NULL;
-        if (profile_level_class)
-          (*env)->DeleteLocalRef (env, profile_level_class);
-        profile_level_class = NULL;
-        if (!valid_codec)
-          break;
+      for (k = 0; k < gst_codec_type->n_profile_levels; k++) {
+        GST_INFO ("Level %d: 0x%08x", k,
+            gst_codec_type->profile_levels[k].level);
+        GST_INFO ("Profile %d: 0x%08x", k,
+            gst_codec_type->profile_levels[k].profile);
       }
 
     next_supported_type:
-      if (color_formats_elems)
-        (*env)->ReleaseIntArrayElements (env, color_formats,
-            color_formats_elems, JNI_ABORT);
-      color_formats_elems = NULL;
-      if (color_formats)
-        (*env)->DeleteLocalRef (env, color_formats);
-      color_formats = NULL;
-      if (profile_levels)
-        (*env)->DeleteLocalRef (env, profile_levels);
-      color_formats = NULL;
       if (capabilities)
-        (*env)->DeleteLocalRef (env, capabilities);
+        gst_amc_codec_capabilities_handle_free (capabilities);
       capabilities = NULL;
-      if (capabilities_class)
-        (*env)->DeleteLocalRef (env, capabilities_class);
-      capabilities_class = NULL;
-      if (supported_type_str)
-        (*env)->ReleaseStringUTFChars (env, supported_type, supported_type_str);
-      supported_type_str = NULL;
-      if (supported_type)
-        (*env)->DeleteLocalRef (env, supported_type);
-      supported_type = NULL;
+      g_clear_error (&error);
       if (!valid_codec)
         break;
     }
@@ -637,20 +378,14 @@ scan_codecs (GstPlugin * plugin)
     /* Clean up of all local references we got */
   next_codec:
     if (name_str)
-      (*env)->ReleaseStringUTFChars (env, name, name_str);
+      g_free (name_str);
     name_str = NULL;
-    if (name)
-      (*env)->DeleteLocalRef (env, name);
-    name = NULL;
     if (supported_types)
-      (*env)->DeleteLocalRef (env, supported_types);
+      g_strfreev (supported_types);
     supported_types = NULL;
     if (codec_info)
-      (*env)->DeleteLocalRef (env, codec_info);
+      gst_amc_codec_info_handle_free (codec_info);
     codec_info = NULL;
-    if (codec_info_class)
-      (*env)->DeleteLocalRef (env, codec_info_class);
-    codec_info_class = NULL;
     if (gst_codec_info) {
       gint j;
 
@@ -667,6 +402,7 @@ scan_codecs (GstPlugin * plugin)
     }
     gst_codec_info = NULL;
     valid_codec = TRUE;
+    g_clear_error (&error);
   }
 
   ret = codec_infos.length != 0;
@@ -759,8 +495,7 @@ scan_codecs (GstPlugin * plugin)
   }
 
 done:
-  if (codec_list_class)
-    (*env)->DeleteLocalRef (env, codec_list_class);
+  g_clear_error (&error);
 
   return ret;
 }
@@ -2084,6 +1819,9 @@ amc_init (GstPlugin * plugin)
 
   gst_amc_codec_info_quark = g_quark_from_static_string ("gst-amc-codec-info");
 
+  if (!gst_amc_codeclist_static_init ())
+    return FALSE;
+
   if (!gst_amc_codec_static_init ())
     return FALSE;
 
index 3700322bde34182bcd9000d99512192bfaa4233e..511665de38a19285cc27b075cb62be81fd8846ed 100644 (file)
 #include <gst/gst.h>
 #include <gst/video/video.h>
 #include <gst/audio/audio.h>
-#include <jni.h>
-
-#include "gstjniutils.h"
 #include "gstamc-codec.h"
+#include "gstamc-codeclist.h"
 #include "gstamc-format.h"
 
 G_BEGIN_DECLS
@@ -39,13 +37,10 @@ struct _GstAmcCodecType {
   gchar *mime;
 
   gint *color_formats;
-  gint n_color_formats;
+  gsize n_color_formats;
 
-  struct {
-    gint profile;
-    gint level;
-  } *profile_levels;
-  gint n_profile_levels;
+  GstAmcCodecProfileLevel * profile_levels;
+  gsize n_profile_levels;
 };
 
 struct _GstAmcCodecInfo {
@@ -56,7 +51,6 @@ struct _GstAmcCodecInfo {
   gint n_supported_types;
 };
 
-
 extern GQuark gst_amc_codec_info_quark;
 
 GstVideoFormat gst_amc_color_format_to_video_format (const GstAmcCodecInfo * codec_info, const gchar * mime, gint color_format);
diff --git a/sys/androidmedia/jni/gstamc-codeclist-jni.c b/sys/androidmedia/jni/gstamc-codeclist-jni.c
new file mode 100644 (file)
index 0000000..7b36091
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2012,2018 Collabora Ltd.
+ *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ * Copyright (C) 2015, Sebastian Dröge <sebastian@centricular.com>
+ *
+ * This library 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
+ * version 2.1 of the License.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../gstjniutils.h"
+#include "../gstamc-codeclist.h"
+
+struct _GstAmcCodecInfoHandle
+{
+  jobject object;
+};
+
+struct _GstAmcCodecCapabilitiesHandle
+{
+  jobject object;
+};
+
+static struct
+{
+  jclass klass;
+  jmethodID get_codec_count;
+  jmethodID get_codec_info_at;
+} media_codeclist;
+
+static struct
+{
+  jclass klass;
+  jmethodID get_capabilities_for_type;
+  jmethodID get_name;
+  jmethodID get_supported_types;
+  jmethodID is_encoder;
+} media_codecinfo;
+
+static struct
+{
+  jclass klass;
+  jfieldID color_formats;
+  jfieldID profile_levels;
+} media_codeccapabilities;
+
+static struct
+{
+  jclass klass;
+  jfieldID level;
+  jfieldID profile;
+} media_codecprofilelevel;
+
+gboolean
+gst_amc_codeclist_static_init (void)
+{
+  JNIEnv *env;
+  GError *err = NULL;
+
+  env = gst_amc_jni_get_env ();
+
+  media_codeclist.klass =
+      gst_amc_jni_get_class (env, &err, "android/media/MediaCodecList");
+  if (!media_codeclist.klass) {
+    GST_ERROR ("Failed to get android.media.MediaCodecList class: %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codeclist.get_codec_count =
+      gst_amc_jni_get_method_id (env, &err, media_codeclist.klass,
+      "getCodecCount", "()I");
+  if (!media_codeclist.get_codec_count) {
+    GST_ERROR ("Failed to get android.media.MediaCodecList getCodecCount(): %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codeclist.get_codec_info_at =
+      gst_amc_jni_get_method_id (env, &err, media_codeclist.klass,
+      "getCodecInfoAt", "(I)Landroid/media/MediaCodecInfo;");
+  if (!media_codeclist.get_codec_count) {
+    GST_ERROR
+        ("Failed to get android.media.MediaCodecList getCodecInfoAt(): %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codecinfo.klass =
+      gst_amc_jni_get_class (env, &err, "android/media/MediaCodecInfo");
+  if (!media_codecinfo.klass) {
+    GST_ERROR ("Failed to get android.media.MediaCodecInfo class: %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codecinfo.get_capabilities_for_type =
+      gst_amc_jni_get_method_id (env, &err, media_codecinfo.klass,
+      "getCapabilitiesForType",
+      "(Ljava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;");
+  if (!media_codecinfo.get_capabilities_for_type) {
+    GST_ERROR
+        ("Failed to get android.media.MediaCodecInfo getCapabilitiesForType(): %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codecinfo.get_name =
+      gst_amc_jni_get_method_id (env, &err, media_codecinfo.klass, "getName",
+      "()Ljava/lang/String;");
+  if (!media_codecinfo.get_name) {
+    GST_ERROR ("Failed to get android.media.MediaCodecInfo getName(): %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codecinfo.get_supported_types =
+      gst_amc_jni_get_method_id (env, &err, media_codecinfo.klass,
+      "getSupportedTypes", "()[Ljava/lang/String;");
+  if (!media_codecinfo.get_supported_types) {
+    GST_ERROR
+        ("Failed to get android.media.MediaCodecInfo getSupportedTypes(): %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codecinfo.is_encoder =
+      gst_amc_jni_get_method_id (env, &err, media_codecinfo.klass, "isEncoder",
+      "()Z");
+  if (!media_codecinfo.is_encoder) {
+    GST_ERROR ("Failed to get android.media.MediaCodecInfo isEncoder(): %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codeccapabilities.klass =
+      gst_amc_jni_get_class (env, &err,
+      "android/media/MediaCodecInfo/CodecCapabilities");
+  if (!media_codeccapabilities.klass) {
+    GST_ERROR
+        ("Failed to get android.media.MediaCodecInfo.CodecCapabilities class: %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codeccapabilities.color_formats =
+      gst_amc_jni_get_field_id (env, &err, media_codeccapabilities.klass,
+      "colorFormats", "[I");
+  if (!media_codeccapabilities.color_formats) {
+    GST_ERROR
+        ("Failed to get android.media.MediaCodecInfo.CodecCapabilities colorFormats: %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codeccapabilities.profile_levels =
+      gst_amc_jni_get_field_id (env, &err, media_codeccapabilities.klass,
+      "profileLevels", "[Landroid/media/MediaCodecInfo$CodecProfileLevel;");
+  if (!media_codeccapabilities.profile_levels) {
+    GST_ERROR
+        ("Failed to get android.media.MediaCodecInfo.CodecCapabilities profileLevels: %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codecprofilelevel.klass =
+      gst_amc_jni_get_class (env, &err,
+      "android/media/MediaCodecInfo/CodecProfileLevel");
+  if (!media_codecprofilelevel.klass) {
+    GST_ERROR
+        ("Failed to get android.media.MediaCodecInfo.CodecProfileLevel class: %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codecprofilelevel.level =
+      gst_amc_jni_get_field_id (env, &err, media_codecprofilelevel.klass,
+      "level", "I");
+  if (!media_codecprofilelevel.level) {
+    GST_ERROR
+        ("Failed to get android.media.MediaCodecInfo.CodecProfileLevel level: %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  media_codecprofilelevel.profile =
+      gst_amc_jni_get_field_id (env, &err, media_codecprofilelevel.klass,
+      "profile", "I");
+  if (!media_codecprofilelevel.profile) {
+    GST_ERROR
+        ("Failed to get android.media.MediaCodecInfo.CodecProfileLevel profile: %s",
+        err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+gboolean
+gst_amc_codeclist_get_count (gint * count, GError ** err)
+{
+  JNIEnv *env;
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_static_int_method (env, err, media_codeclist.klass,
+          media_codeclist.get_codec_count, count))
+    return FALSE;
+
+  return TRUE;
+}
+
+GstAmcCodecInfoHandle *
+gst_amc_codeclist_get_codec_info_at (gint index, GError ** err)
+{
+  GstAmcCodecInfoHandle *ret;
+  jobject object;
+  JNIEnv *env;
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_static_object_method (env, err, media_codeclist.klass,
+          media_codeclist.get_codec_info_at, &object, index))
+    return NULL;
+
+  ret = g_new0 (GstAmcCodecInfoHandle, 1);
+  ret->object = object;
+  return ret;
+}
+
+void
+gst_amc_codec_info_handle_free (GstAmcCodecInfoHandle * handle)
+{
+  JNIEnv *env;
+
+  g_return_if_fail (handle != NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  if (handle->object)
+    gst_amc_jni_object_unref (env, handle->object);
+  g_free (handle);
+}
+
+gchar *
+gst_amc_codec_info_handle_get_name (GstAmcCodecInfoHandle * handle,
+    GError ** err)
+{
+  JNIEnv *env;
+  jstring v_str = NULL;
+
+  g_return_val_if_fail (handle != NULL, NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_object_method (env, err, handle->object,
+          media_codecinfo.get_name, &v_str))
+    return NULL;
+
+  return gst_amc_jni_string_to_gchar (env, v_str, TRUE);
+}
+
+gboolean
+gst_amc_codec_info_handle_is_encoder (GstAmcCodecInfoHandle * handle,
+    gboolean * is_encoder, GError ** err)
+{
+  JNIEnv *env;
+
+  g_return_val_if_fail (handle != NULL, FALSE);
+  g_return_val_if_fail (is_encoder != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_boolean_method (env, err, handle->object,
+          media_codecinfo.is_encoder, is_encoder))
+    return FALSE;
+
+  return TRUE;
+}
+
+gchar **
+gst_amc_codec_info_handle_get_supported_types (GstAmcCodecInfoHandle * handle,
+    gsize * length, GError ** err)
+{
+  JNIEnv *env;
+  jarray array = NULL;
+  jsize len;
+  jsize i;
+  gchar **strv = NULL;
+
+  g_return_val_if_fail (handle != NULL, NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_object_method (env, err, handle->object,
+          media_codecinfo.get_name, &array))
+    goto done;
+
+  len = (*env)->GetArrayLength (env, array);
+  if ((*env)->ExceptionCheck (env)) {
+    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+        GST_LIBRARY_ERROR_FAILED, "Failed to get array length");
+    goto done;
+  }
+
+  strv = g_new0 (gchar *, len + 1);
+  *length = len;
+
+  for (i = 0; i < len; i++) {
+    jstring string;
+
+    string = (*env)->GetObjectArrayElement (env, array, i);
+    if ((*env)->ExceptionCheck (env)) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED, "Failed to get array element");
+      g_strfreev (strv);
+      strv = NULL;
+      goto done;
+    }
+
+    strv[i] = gst_amc_jni_string_to_gchar (env, string, TRUE);
+    if (!strv[i]) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED, "Failed create string");
+      g_strfreev (strv);
+      strv = NULL;
+      goto done;
+    }
+  }
+
+done:
+  if (array)
+    (*env)->DeleteLocalRef (env, array);
+
+  return strv;
+}
+
+GstAmcCodecCapabilitiesHandle *
+gst_amc_codec_info_handle_get_capabilities_for_type (GstAmcCodecInfoHandle *
+    handle, const gchar * type, GError ** err)
+{
+  GstAmcCodecCapabilitiesHandle *ret = NULL;
+  jstring type_str;
+  jobject object;
+  JNIEnv *env;
+
+  env = gst_amc_jni_get_env ();
+
+  type_str = gst_amc_jni_string_from_gchar (env, err, FALSE, type);
+  if (!type_str)
+    goto done;
+
+  if (!gst_amc_jni_call_object_method (env, err, handle->object,
+          media_codecinfo.get_capabilities_for_type, &object, type_str))
+    goto done;
+
+  ret = g_new0 (GstAmcCodecCapabilitiesHandle, 1);
+  ret->object = object;
+
+done:
+  if (type_str)
+    gst_amc_jni_object_local_unref (env, type_str);
+
+  return ret;
+}
+
+void
+gst_amc_codec_capabilities_handle_free (GstAmcCodecCapabilitiesHandle * handle)
+{
+  JNIEnv *env;
+
+  g_return_if_fail (handle != NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  if (handle->object)
+    gst_amc_jni_object_unref (env, handle->object);
+  g_free (handle);
+}
+
+gint *gst_amc_codec_capabilities_handle_get_color_formats
+    (GstAmcCodecCapabilitiesHandle * handle, gsize * length, GError ** err)
+{
+  JNIEnv *env;
+  jarray array = NULL;
+  jsize len;
+  jint *elems = NULL;
+  gint *ret = NULL;
+
+  g_return_val_if_fail (handle != NULL, NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_get_object_field (env, err, handle->object,
+          media_codeccapabilities.color_formats, &array))
+    goto done;
+
+  len = (*env)->GetArrayLength (env, array);
+  if ((*env)->ExceptionCheck (env)) {
+    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+        GST_LIBRARY_ERROR_FAILED, "Failed to get array length");
+    goto done;
+  }
+
+  elems = (*env)->GetIntArrayElements (env, array, NULL);
+  if ((*env)->ExceptionCheck (env)) {
+    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+        GST_LIBRARY_ERROR_FAILED, "Failed to get array elements");
+    goto done;
+  }
+
+  ret = g_memdup (elems, sizeof (jint) * len);
+  *length = len;
+
+done:
+  if (elems)
+    (*env)->ReleaseIntArrayElements (env, array, elems, JNI_ABORT);
+  if (array)
+    (*env)->DeleteLocalRef (env, array);
+
+  return ret;
+}
+
+GstAmcCodecProfileLevel *gst_amc_codec_capabilities_handle_get_profile_levels
+    (GstAmcCodecCapabilitiesHandle * handle, gsize * length, GError ** err)
+{
+  GstAmcCodecProfileLevel *ret = NULL;
+  JNIEnv *env;
+  jobject array = NULL;
+  jsize len;
+  jsize i;
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_get_object_field (env, err, handle->object,
+          media_codeccapabilities.profile_levels, &array))
+    goto done;
+
+  len = (*env)->GetArrayLength (env, array);
+  if ((*env)->ExceptionCheck (env)) {
+    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+        GST_LIBRARY_ERROR_FAILED, "Failed to get array length");
+    goto done;
+  }
+
+  ret = g_new0 (GstAmcCodecProfileLevel, len);
+  *length = len;
+
+  for (i = 0; i < len; i++) {
+    jobject object = NULL;
+
+    object = (*env)->GetObjectArrayElement (env, array, i);
+    if ((*env)->ExceptionCheck (env)) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED, "Failed to get array element");
+      g_free (ret);
+      ret = NULL;
+      goto done;
+    }
+
+    if (!gst_amc_jni_get_int_field (env, err, object,
+            media_codecprofilelevel.level, &ret[i].level)) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED, "Failed to get level");
+      (*env)->DeleteLocalRef (env, object);
+      g_free (ret);
+      ret = NULL;
+      goto done;
+    }
+
+    if (!gst_amc_jni_get_int_field (env, err, object,
+            media_codecprofilelevel.profile, &ret[i].profile)) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED, "Failed to get profile");
+      (*env)->DeleteLocalRef (env, object);
+      g_free (ret);
+      ret = NULL;
+      goto done;
+    }
+
+    (*env)->DeleteLocalRef (env, object);
+  }
+
+done:
+  if (array)
+    (*env)->DeleteLocalRef (env, array);
+
+  return ret;
+}
index 79bc2f8736b9714ba3209d36715fc3c685a43dd7..994faf79f219cd708390d2a74c50a41f8217dfe9 100644 (file)
@@ -12,6 +12,7 @@ androidmedia_sources = [
   'gst-android-hardware-sensor.c',
   'gstjniutils.c',
   'jni/gstamc-codec-jni.c',
+  'jni/gstamc-codeclist-jni.c',
   'jni/gstamc-format-jni.c',
 ]