amc: Move MediaCodec JNI wrapper into its own module
authorXavier Claessens <xavier.claessens@collabora.com>
Sat, 10 Nov 2018 21:51:02 +0000 (16:51 -0500)
committerXavier Claessens <xavier.claessens@collabora.com>
Tue, 20 Aug 2019 12:53:55 +0000 (08:53 -0400)
This will facilitate adding another implementation based on
NdkMediaCodec instead of JNI.

sys/androidmedia/gstamc-codec.h [new file with mode: 0644]
sys/androidmedia/gstamc-format.h [new file with mode: 0644]
sys/androidmedia/gstamc.c
sys/androidmedia/gstamc.h
sys/androidmedia/gstjniutils.c
sys/androidmedia/gstjniutils.h
sys/androidmedia/jni/gstamc-codec-jni.c [new file with mode: 0644]
sys/androidmedia/jni/gstamc-format-jni.c [new file with mode: 0644]
sys/androidmedia/jni/gstamc-internal-jni.h [new file with mode: 0644]
sys/androidmedia/meson.build

diff --git a/sys/androidmedia/gstamc-codec.h b/sys/androidmedia/gstamc-codec.h
new file mode 100644 (file)
index 0000000..442451a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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_CODEC_H__
+#define __GST_AMC_CODEC_H__
+
+#include <gst/gst.h>
+
+#include "gstamc-format.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GstAmcBuffer GstAmcBuffer;
+typedef struct _GstAmcBufferInfo GstAmcBufferInfo;
+typedef struct _GstAmcCodec GstAmcCodec;
+
+struct _GstAmcBuffer {
+  guint8 *data;
+  gsize size;
+};
+
+struct _GstAmcBufferInfo {
+  gint flags;
+  gint offset;
+  gint64 presentation_time_us;
+  gint size;
+};
+
+gboolean gst_amc_codec_static_init (void);
+
+void gst_amc_buffer_free (GstAmcBuffer * buffer);
+gboolean gst_amc_buffer_set_position_and_limit (GstAmcBuffer * buffer, GError ** err,
+    gint position, gint limit);
+
+GstAmcCodec * gst_amc_codec_new (const gchar *name, GError **err);
+void gst_amc_codec_free (GstAmcCodec * codec);
+
+gboolean gst_amc_codec_configure (GstAmcCodec * codec, GstAmcFormat * format, jobject surface, gint flags, GError **err);
+GstAmcFormat * gst_amc_codec_get_output_format (GstAmcCodec * codec, GError **err);
+
+gboolean gst_amc_codec_start (GstAmcCodec * codec, GError **err);
+gboolean gst_amc_codec_stop (GstAmcCodec * codec, GError **err);
+gboolean gst_amc_codec_flush (GstAmcCodec * codec, GError **err);
+gboolean gst_amc_codec_release (GstAmcCodec * codec, GError **err);
+
+GstAmcBuffer * gst_amc_codec_get_output_buffer (GstAmcCodec * codec, gint index, GError **err);
+GstAmcBuffer * gst_amc_codec_get_input_buffer (GstAmcCodec * codec, gint index, GError **err);
+
+gint gst_amc_codec_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs, GError **err);
+gint gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec, GstAmcBufferInfo *info, gint64 timeoutUs, GError **err);
+
+gboolean gst_amc_codec_queue_input_buffer (GstAmcCodec * codec, gint index, const GstAmcBufferInfo *info, GError **err);
+gboolean gst_amc_codec_release_output_buffer (GstAmcCodec * codec, gint index, gboolean render, GError **err);
+
+G_END_DECLS
+
+#endif /* __GST_AMC_CODEC_H__ */
diff --git a/sys/androidmedia/gstamc-format.h b/sys/androidmedia/gstamc-format.h
new file mode 100644 (file)
index 0000000..da0cf26
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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_FORMAT_H__
+#define __GST_AMC_FORMAT_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstAmcFormat GstAmcFormat;
+typedef struct _GstAmcColorFormatInfo GstAmcColorFormatInfo;
+
+gboolean gst_amc_format_static_init (void);
+
+GstAmcFormat * gst_amc_format_new_audio (const gchar *mime, gint sample_rate, gint channels, GError **err);
+GstAmcFormat * gst_amc_format_new_video (const gchar *mime, gint width, gint height, GError **err);
+void gst_amc_format_free (GstAmcFormat * format);
+
+gchar * gst_amc_format_to_string (GstAmcFormat * format, GError **err);
+
+gboolean gst_amc_format_contains_key (GstAmcFormat *format, const gchar *key, GError **err);
+
+gboolean gst_amc_format_get_float (GstAmcFormat *format, const gchar *key, gfloat *value, GError **err);
+gboolean gst_amc_format_set_float (GstAmcFormat *format, const gchar *key, gfloat value, GError **err);
+gboolean gst_amc_format_get_int (GstAmcFormat *format, const gchar *key, gint *value, GError **err);
+gboolean gst_amc_format_set_int (GstAmcFormat *format, const gchar *key, gint value, GError **err);
+gboolean gst_amc_format_get_string (GstAmcFormat *format, const gchar *key, gchar **value, GError **err);
+gboolean gst_amc_format_set_string (GstAmcFormat *format, const gchar *key, const gchar *value, GError **err);
+gboolean gst_amc_format_get_buffer (GstAmcFormat *format, const gchar *key, guint8 **data, gsize *size, GError **err);
+gboolean gst_amc_format_set_buffer (GstAmcFormat *format, const gchar *key, guint8 *data, gsize size, GError **err);
+
+G_END_DECLS
+
+#endif /* __GST_AMC_FORMAT_H__ */
index f28dbfd..fbf50d6 100644 (file)
@@ -60,1262 +60,6 @@ static gboolean ignore_unknown_color_formats = FALSE;
 static gboolean accepted_color_formats (GstAmcCodecType * type,
     gboolean is_encoder);
 
-/* Global cached references */
-static struct
-{
-  jclass klass;
-  jmethodID constructor;
-} java_string;
-static struct
-{
-  jclass klass;
-  jmethodID configure;
-  jmethodID create_by_codec_name;
-  jmethodID dequeue_input_buffer;
-  jmethodID dequeue_output_buffer;
-  jmethodID flush;
-  jmethodID get_input_buffers;
-  jmethodID get_input_buffer;
-  jmethodID get_output_buffers;
-  jmethodID get_output_buffer;
-  jmethodID get_output_format;
-  jmethodID queue_input_buffer;
-  jmethodID release;
-  jmethodID release_output_buffer;
-  jmethodID start;
-  jmethodID stop;
-} media_codec;
-static struct
-{
-  jclass klass;
-  jmethodID constructor;
-  jfieldID flags;
-  jfieldID offset;
-  jfieldID presentation_time_us;
-  jfieldID size;
-} media_codec_buffer_info;
-static struct
-{
-  jclass klass;
-  jmethodID create_audio_format;
-  jmethodID create_video_format;
-  jmethodID to_string;
-  jmethodID contains_key;
-  jmethodID get_float;
-  jmethodID set_float;
-  jmethodID get_integer;
-  jmethodID set_integer;
-  jmethodID get_string;
-  jmethodID set_string;
-  jmethodID get_byte_buffer;
-  jmethodID set_byte_buffer;
-} media_format;
-
-static GstAmcBuffer *gst_amc_codec_get_input_buffers (GstAmcCodec * codec,
-    gsize * n_buffers, GError ** err);
-static GstAmcBuffer *gst_amc_codec_get_output_buffers (GstAmcCodec * codec,
-    gsize * n_buffers, GError ** err);
-
-GstAmcCodec *
-gst_amc_codec_new (const gchar * name, GError ** err)
-{
-  JNIEnv *env;
-  GstAmcCodec *codec = NULL;
-  jstring name_str;
-  jobject object = NULL;
-
-  g_return_val_if_fail (name != NULL, NULL);
-
-  env = gst_amc_jni_get_env ();
-
-  name_str = gst_amc_jni_string_from_gchar (env, err, FALSE, name);
-  if (!name_str) {
-    goto error;
-  }
-
-  codec = g_slice_new0 (GstAmcCodec);
-
-  if (!gst_amc_jni_call_static_object_method (env, err, media_codec.klass,
-          media_codec.create_by_codec_name, &object, name_str))
-    goto error;
-
-  codec->object = gst_amc_jni_object_make_global (env, object);
-  object = NULL;
-
-  if (!codec->object) {
-    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
-        GST_LIBRARY_ERROR_SETTINGS, "Failed to create global codec reference");
-    goto error;
-  }
-
-done:
-  if (name_str)
-    gst_amc_jni_object_local_unref (env, name_str);
-  name_str = NULL;
-
-  return codec;
-
-error:
-  if (codec)
-    g_slice_free (GstAmcCodec, codec);
-  codec = NULL;
-  goto done;
-}
-
-void
-gst_amc_codec_free (GstAmcCodec * codec)
-{
-  JNIEnv *env;
-
-  g_return_if_fail (codec != NULL);
-
-  env = gst_amc_jni_get_env ();
-
-  if (codec->input_buffers)
-    gst_amc_jni_free_buffer_array (env, codec->input_buffers,
-        codec->n_input_buffers);
-  codec->input_buffers = NULL;
-  codec->n_input_buffers = 0;
-
-  if (codec->output_buffers)
-    gst_amc_jni_free_buffer_array (env, codec->output_buffers,
-        codec->n_output_buffers);
-  codec->output_buffers = NULL;
-  codec->n_output_buffers = 0;
-
-  gst_amc_jni_object_unref (env, codec->object);
-  g_slice_free (GstAmcCodec, codec);
-}
-
-gboolean
-gst_amc_codec_configure (GstAmcCodec * codec, GstAmcFormat * format,
-    jobject surface, gint flags, GError ** err)
-{
-  JNIEnv *env;
-
-  g_return_val_if_fail (codec != NULL, FALSE);
-  g_return_val_if_fail (format != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-  return gst_amc_jni_call_void_method (env, err, codec->object,
-      media_codec.configure, format->object, surface, NULL, flags);
-}
-
-GstAmcFormat *
-gst_amc_codec_get_output_format (GstAmcCodec * codec, GError ** err)
-{
-  JNIEnv *env;
-  GstAmcFormat *ret = NULL;
-  jobject object = NULL;
-
-  g_return_val_if_fail (codec != NULL, NULL);
-
-  env = gst_amc_jni_get_env ();
-
-  if (!gst_amc_jni_call_object_method (env, err, codec->object,
-          media_codec.get_output_format, &object))
-    goto done;
-
-  ret = g_slice_new0 (GstAmcFormat);
-
-  ret->object = gst_amc_jni_object_make_global (env, object);
-  if (!ret->object) {
-    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
-        GST_LIBRARY_ERROR_SETTINGS, "Failed to create global format reference");
-    g_slice_free (GstAmcFormat, ret);
-    ret = NULL;
-  }
-
-done:
-
-  return ret;
-}
-
-gboolean
-gst_amc_codec_start (GstAmcCodec * codec, GError ** err)
-{
-  JNIEnv *env;
-  gboolean ret;
-
-  g_return_val_if_fail (codec != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-  ret = gst_amc_jni_call_void_method (env, err, codec->object,
-      media_codec.start);
-  if (!ret)
-    return ret;
-
-  if (!media_codec.get_input_buffer) {
-    if (codec->input_buffers)
-      gst_amc_jni_free_buffer_array (env, codec->input_buffers,
-          codec->n_input_buffers);
-    codec->input_buffers =
-        gst_amc_codec_get_input_buffers (codec, &codec->n_input_buffers, err);
-    if (!codec->input_buffers) {
-      gst_amc_codec_stop (codec, NULL);
-      return FALSE;
-    }
-  }
-
-  return ret;
-}
-
-gboolean
-gst_amc_codec_stop (GstAmcCodec * codec, GError ** err)
-{
-  JNIEnv *env;
-
-  g_return_val_if_fail (codec != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  if (codec->input_buffers)
-    gst_amc_jni_free_buffer_array (env, codec->input_buffers,
-        codec->n_input_buffers);
-  codec->input_buffers = NULL;
-  codec->n_input_buffers = 0;
-
-  if (codec->output_buffers)
-    gst_amc_jni_free_buffer_array (env, codec->output_buffers,
-        codec->n_output_buffers);
-  codec->output_buffers = NULL;
-  codec->n_output_buffers = 0;
-
-  return gst_amc_jni_call_void_method (env, err, codec->object,
-      media_codec.stop);
-}
-
-gboolean
-gst_amc_codec_flush (GstAmcCodec * codec, GError ** err)
-{
-  JNIEnv *env;
-
-  g_return_val_if_fail (codec != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-  return gst_amc_jni_call_void_method (env, err, codec->object,
-      media_codec.flush);
-}
-
-gboolean
-gst_amc_codec_release (GstAmcCodec * codec, GError ** err)
-{
-  JNIEnv *env;
-
-  g_return_val_if_fail (codec != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  if (codec->input_buffers)
-    gst_amc_jni_free_buffer_array (env, codec->input_buffers,
-        codec->n_input_buffers);
-  codec->input_buffers = NULL;
-  codec->n_input_buffers = 0;
-
-  if (codec->output_buffers)
-    gst_amc_jni_free_buffer_array (env, codec->output_buffers,
-        codec->n_output_buffers);
-  codec->output_buffers = NULL;
-  codec->n_output_buffers = 0;
-
-  return gst_amc_jni_call_void_method (env, err, codec->object,
-      media_codec.release);
-}
-
-static GstAmcBuffer *
-gst_amc_codec_get_output_buffers (GstAmcCodec * codec, gsize * n_buffers,
-    GError ** err)
-{
-  JNIEnv *env;
-  jobject output_buffers = NULL;
-  GstAmcBuffer *ret = NULL;
-
-  g_return_val_if_fail (codec != NULL, NULL);
-  g_return_val_if_fail (n_buffers != NULL, NULL);
-
-  *n_buffers = 0;
-  env = gst_amc_jni_get_env ();
-
-  if (!gst_amc_jni_call_object_method (env, err, codec->object,
-          media_codec.get_output_buffers, &output_buffers))
-    goto done;
-
-  gst_amc_jni_get_buffer_array (env, err, output_buffers, &ret, n_buffers);
-
-done:
-  if (output_buffers)
-    gst_amc_jni_object_local_unref (env, output_buffers);
-
-  return ret;
-}
-
-GstAmcBuffer *
-gst_amc_codec_get_output_buffer (GstAmcCodec * codec, gint index, GError ** err)
-{
-  JNIEnv *env;
-  jobject buffer = NULL;
-  GstAmcBuffer *ret = NULL;
-
-  g_return_val_if_fail (codec != NULL, NULL);
-  g_return_val_if_fail (index >= 0, NULL);
-
-  env = gst_amc_jni_get_env ();
-
-  if (!media_codec.get_output_buffer) {
-    g_return_val_if_fail (index < codec->n_output_buffers && index >= 0, NULL);
-    if (codec->output_buffers[index].object)
-      return gst_amc_buffer_copy (&codec->output_buffers[index]);
-    else
-      return NULL;
-  }
-
-  if (!gst_amc_jni_call_object_method (env, err, codec->object,
-          media_codec.get_output_buffer, &buffer, index))
-    goto done;
-
-  if (buffer != NULL) {
-    ret = g_new0 (GstAmcBuffer, 1);
-    ret->object = gst_amc_jni_object_make_global (env, buffer);
-    if (!ret->object) {
-      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
-          GST_LIBRARY_ERROR_FAILED, "Failed to create global buffer reference");
-      goto error;
-    }
-
-    ret->data = (*env)->GetDirectBufferAddress (env, ret->object);
-    if (!ret->data) {
-      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
-          GST_LIBRARY_ERROR_FAILED, "Failed to get buffer address");
-      goto error;
-    }
-    ret->size = (*env)->GetDirectBufferCapacity (env, ret->object);
-  }
-
-done:
-
-  return ret;
-
-error:
-  if (ret->object)
-    gst_amc_jni_object_unref (env, ret->object);
-  g_free (ret);
-
-  return NULL;
-}
-
-static GstAmcBuffer *
-gst_amc_codec_get_input_buffers (GstAmcCodec * codec, gsize * n_buffers,
-    GError ** err)
-{
-  JNIEnv *env;
-  jobject input_buffers = NULL;
-  GstAmcBuffer *ret = NULL;
-
-  g_return_val_if_fail (codec != NULL, NULL);
-  g_return_val_if_fail (n_buffers != NULL, NULL);
-
-  *n_buffers = 0;
-  env = gst_amc_jni_get_env ();
-
-  if (!gst_amc_jni_call_object_method (env, err, codec->object,
-          media_codec.get_input_buffers, &input_buffers))
-    goto done;
-
-  gst_amc_jni_get_buffer_array (env, err, input_buffers, &ret, n_buffers);
-
-done:
-  if (input_buffers)
-    gst_amc_jni_object_local_unref (env, input_buffers);
-
-  return ret;
-}
-
-GstAmcBuffer *
-gst_amc_codec_get_input_buffer (GstAmcCodec * codec, gint index, GError ** err)
-{
-  JNIEnv *env;
-  jobject buffer = NULL;
-  GstAmcBuffer *ret = NULL;
-
-  g_return_val_if_fail (codec != NULL, NULL);
-  g_return_val_if_fail (index >= 0, NULL);
-
-  env = gst_amc_jni_get_env ();
-
-  if (!media_codec.get_input_buffer) {
-    g_return_val_if_fail (index < codec->n_input_buffers && index >= 0, NULL);
-    if (codec->input_buffers[index].object)
-      return gst_amc_buffer_copy (&codec->input_buffers[index]);
-    else
-      return NULL;
-  }
-
-  if (!gst_amc_jni_call_object_method (env, err, codec->object,
-          media_codec.get_input_buffer, &buffer, index))
-    goto done;
-
-  if (buffer != NULL) {
-    ret = g_new0 (GstAmcBuffer, 1);
-    ret->object = gst_amc_jni_object_make_global (env, buffer);
-    if (!ret->object) {
-      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
-          GST_LIBRARY_ERROR_FAILED, "Failed to create global buffer reference");
-      goto error;
-    }
-
-    ret->data = (*env)->GetDirectBufferAddress (env, ret->object);
-    if (!ret->data) {
-      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
-          GST_LIBRARY_ERROR_FAILED, "Failed to get buffer address");
-      goto error;
-    }
-    ret->size = (*env)->GetDirectBufferCapacity (env, ret->object);
-  }
-
-done:
-
-  return ret;
-
-error:
-  if (ret->object)
-    gst_amc_jni_object_unref (env, ret->object);
-  g_free (ret);
-
-  return NULL;
-}
-
-gint
-gst_amc_codec_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs,
-    GError ** err)
-{
-  JNIEnv *env;
-  gint ret = G_MININT;
-
-  g_return_val_if_fail (codec != NULL, G_MININT);
-
-  env = gst_amc_jni_get_env ();
-  if (!gst_amc_jni_call_int_method (env, err, codec->object,
-          media_codec.dequeue_input_buffer, &ret, timeoutUs))
-    return G_MININT;
-
-  return ret;
-}
-
-static gboolean
-gst_amc_codec_fill_buffer_info (JNIEnv * env, jobject buffer_info,
-    GstAmcBufferInfo * info, GError ** err)
-{
-  g_return_val_if_fail (buffer_info != NULL, FALSE);
-
-  if (!gst_amc_jni_get_int_field (env, err, buffer_info,
-          media_codec_buffer_info.flags, &info->flags))
-    return FALSE;
-
-  if (!gst_amc_jni_get_int_field (env, err, buffer_info,
-          media_codec_buffer_info.offset, &info->offset))
-    return FALSE;
-
-  if (!gst_amc_jni_get_long_field (env, err, buffer_info,
-          media_codec_buffer_info.presentation_time_us,
-          &info->presentation_time_us))
-    return FALSE;
-
-  if (!gst_amc_jni_get_int_field (env, err, buffer_info,
-          media_codec_buffer_info.size, &info->size))
-    return FALSE;
-
-  return TRUE;
-}
-
-gint
-gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec,
-    GstAmcBufferInfo * info, gint64 timeoutUs, GError ** err)
-{
-  JNIEnv *env;
-  gint ret = G_MININT;
-  jobject info_o = NULL;
-
-  g_return_val_if_fail (codec != NULL, G_MININT);
-
-  env = gst_amc_jni_get_env ();
-
-  info_o =
-      gst_amc_jni_new_object (env, err, FALSE, media_codec_buffer_info.klass,
-      media_codec_buffer_info.constructor);
-  if (!info_o)
-    goto done;
-
-  if (!gst_amc_jni_call_int_method (env, err, codec->object,
-          media_codec.dequeue_output_buffer, &ret, info_o, timeoutUs)) {
-    ret = G_MININT;
-    goto done;
-  }
-
-  if (ret == INFO_OUTPUT_BUFFERS_CHANGED || ret == INFO_OUTPUT_FORMAT_CHANGED
-      || (ret >= 0 && !codec->output_buffers
-          && !media_codec.get_output_buffer)) {
-    if (!media_codec.get_output_buffer) {
-      if (codec->output_buffers)
-        gst_amc_jni_free_buffer_array (env, codec->output_buffers,
-            codec->n_output_buffers);
-      codec->output_buffers =
-          gst_amc_codec_get_output_buffers (codec,
-          &codec->n_output_buffers, err);
-      if (!codec->output_buffers) {
-        ret = G_MININT;
-        goto done;
-      }
-    }
-    if (ret == INFO_OUTPUT_BUFFERS_CHANGED) {
-      gst_amc_jni_object_local_unref (env, info_o);
-      return gst_amc_codec_dequeue_output_buffer (codec, info, timeoutUs, err);
-    }
-  } else if (ret < 0) {
-    goto done;
-  }
-
-  if (ret >= 0 && !gst_amc_codec_fill_buffer_info (env, info_o, info, err)) {
-    ret = G_MININT;
-    goto done;
-  }
-
-done:
-  if (info_o)
-    gst_amc_jni_object_local_unref (env, info_o);
-  info_o = NULL;
-
-  return ret;
-}
-
-gboolean
-gst_amc_codec_queue_input_buffer (GstAmcCodec * codec, gint index,
-    const GstAmcBufferInfo * info, GError ** err)
-{
-  JNIEnv *env;
-
-  g_return_val_if_fail (codec != NULL, FALSE);
-  g_return_val_if_fail (info != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-  return gst_amc_jni_call_void_method (env, err, codec->object,
-      media_codec.queue_input_buffer, index, info->offset, info->size,
-      info->presentation_time_us, info->flags);
-}
-
-gboolean
-gst_amc_codec_release_output_buffer (GstAmcCodec * codec, gint index,
-    gboolean render, GError ** err)
-{
-  JNIEnv *env;
-
-  g_return_val_if_fail (codec != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-  return gst_amc_jni_call_void_method (env, err, codec->object,
-      media_codec.release_output_buffer, index, render);
-}
-
-GstAmcFormat *
-gst_amc_format_new_audio (const gchar * mime, gint sample_rate, gint channels,
-    GError ** err)
-{
-  JNIEnv *env;
-  GstAmcFormat *format = NULL;
-  jstring mime_str;
-
-  g_return_val_if_fail (mime != NULL, NULL);
-
-  env = gst_amc_jni_get_env ();
-
-  mime_str = gst_amc_jni_string_from_gchar (env, err, FALSE, mime);
-  if (!mime_str)
-    goto error;
-
-  format = g_slice_new0 (GstAmcFormat);
-  format->object =
-      gst_amc_jni_new_object_from_static (env, err, TRUE, media_format.klass,
-      media_format.create_audio_format, mime_str, sample_rate, channels);
-  if (!format->object)
-    goto error;
-
-done:
-  if (mime_str)
-    gst_amc_jni_object_local_unref (env, mime_str);
-  mime_str = NULL;
-
-  return format;
-
-error:
-  if (format)
-    g_slice_free (GstAmcFormat, format);
-  format = NULL;
-  goto done;
-}
-
-GstAmcFormat *
-gst_amc_format_new_video (const gchar * mime, gint width, gint height,
-    GError ** err)
-{
-  JNIEnv *env;
-  GstAmcFormat *format = NULL;
-  jstring mime_str;
-
-  g_return_val_if_fail (mime != NULL, NULL);
-
-  env = gst_amc_jni_get_env ();
-
-  mime_str = gst_amc_jni_string_from_gchar (env, err, FALSE, mime);
-  if (!mime_str)
-    goto error;
-
-  format = g_slice_new0 (GstAmcFormat);
-  format->object =
-      gst_amc_jni_new_object_from_static (env, err, TRUE, media_format.klass,
-      media_format.create_video_format, mime_str, width, height);
-  if (!format->object)
-    goto error;
-
-done:
-  if (mime_str)
-    gst_amc_jni_object_local_unref (env, mime_str);
-  mime_str = NULL;
-
-  return format;
-
-error:
-  if (format)
-    g_slice_free (GstAmcFormat, format);
-  format = NULL;
-  goto done;
-}
-
-void
-gst_amc_format_free (GstAmcFormat * format)
-{
-  JNIEnv *env;
-
-  g_return_if_fail (format != NULL);
-
-  env = gst_amc_jni_get_env ();
-  gst_amc_jni_object_unref (env, format->object);
-  g_slice_free (GstAmcFormat, format);
-}
-
-gchar *
-gst_amc_format_to_string (GstAmcFormat * format, GError ** err)
-{
-  JNIEnv *env;
-  jstring v_str = NULL;
-  gchar *ret = NULL;
-
-  g_return_val_if_fail (format != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  if (!gst_amc_jni_call_object_method (env, err, format->object,
-          media_format.to_string, &v_str))
-    goto done;
-  ret = gst_amc_jni_string_to_gchar (env, v_str, TRUE);
-
-done:
-
-  return ret;
-}
-
-gboolean
-gst_amc_format_contains_key (GstAmcFormat * format, const gchar * key,
-    GError ** err)
-{
-  JNIEnv *env;
-  gboolean ret = FALSE;
-  jstring key_str = NULL;
-
-  g_return_val_if_fail (format != NULL, FALSE);
-  g_return_val_if_fail (key != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
-  if (!key_str)
-    goto done;
-
-  if (!gst_amc_jni_call_boolean_method (env, err, format->object,
-          media_format.contains_key, &ret, key_str))
-    goto done;
-
-done:
-  if (key_str)
-    gst_amc_jni_object_local_unref (env, key_str);
-
-  return ret;
-}
-
-gboolean
-gst_amc_format_get_float (GstAmcFormat * format, const gchar * key,
-    gfloat * value, GError ** err)
-{
-  JNIEnv *env;
-  gboolean ret = FALSE;
-  jstring key_str = NULL;
-
-  g_return_val_if_fail (format != NULL, FALSE);
-  g_return_val_if_fail (key != NULL, FALSE);
-  g_return_val_if_fail (value != NULL, FALSE);
-
-  *value = 0;
-  env = gst_amc_jni_get_env ();
-
-  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
-  if (!key_str)
-    goto done;
-
-  if (!gst_amc_jni_call_float_method (env, err, format->object,
-          media_format.get_float, value, key_str))
-    goto done;
-  ret = TRUE;
-
-done:
-  if (key_str)
-    gst_amc_jni_object_local_unref (env, key_str);
-
-  return ret;
-}
-
-gboolean
-gst_amc_format_set_float (GstAmcFormat * format, const gchar * key,
-    gfloat value, GError ** err)
-{
-  JNIEnv *env;
-  jstring key_str = NULL;
-  gboolean ret = FALSE;
-
-  g_return_val_if_fail (format != NULL, FALSE);
-  g_return_val_if_fail (key != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
-  if (!key_str)
-    goto done;
-
-  if (!gst_amc_jni_call_void_method (env, err, format->object,
-          media_format.set_float, key_str, value))
-    goto done;
-
-  ret = TRUE;
-
-done:
-  if (key_str)
-    gst_amc_jni_object_local_unref (env, key_str);
-
-  return ret;
-}
-
-gboolean
-gst_amc_format_get_int (GstAmcFormat * format, const gchar * key, gint * value,
-    GError ** err)
-{
-  JNIEnv *env;
-  gboolean ret = FALSE;
-  jstring key_str = NULL;
-
-  g_return_val_if_fail (format != NULL, FALSE);
-  g_return_val_if_fail (key != NULL, FALSE);
-  g_return_val_if_fail (value != NULL, FALSE);
-
-  *value = 0;
-  env = gst_amc_jni_get_env ();
-
-  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
-  if (!key_str)
-    goto done;
-
-  if (!gst_amc_jni_call_int_method (env, err, format->object,
-          media_format.get_integer, value, key_str))
-    goto done;
-  ret = TRUE;
-
-done:
-  if (key_str)
-    gst_amc_jni_object_local_unref (env, key_str);
-
-  return ret;
-
-}
-
-gboolean
-gst_amc_format_set_int (GstAmcFormat * format, const gchar * key, gint value,
-    GError ** err)
-{
-  JNIEnv *env;
-  jstring key_str = NULL;
-  gboolean ret = FALSE;
-
-  g_return_val_if_fail (format != NULL, FALSE);
-  g_return_val_if_fail (key != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
-  if (!key_str)
-    goto done;
-
-  if (!gst_amc_jni_call_void_method (env, err, format->object,
-          media_format.set_integer, key_str, value))
-    goto done;
-
-  ret = TRUE;
-
-done:
-  if (key_str)
-    gst_amc_jni_object_local_unref (env, key_str);
-
-  return ret;
-}
-
-gboolean
-gst_amc_format_get_string (GstAmcFormat * format, const gchar * key,
-    gchar ** value, GError ** err)
-{
-  JNIEnv *env;
-  gboolean ret = FALSE;
-  jstring key_str = NULL;
-  jstring v_str = NULL;
-
-  g_return_val_if_fail (format != NULL, FALSE);
-  g_return_val_if_fail (key != NULL, FALSE);
-  g_return_val_if_fail (value != NULL, FALSE);
-
-  *value = 0;
-  env = gst_amc_jni_get_env ();
-
-  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
-  if (!key_str)
-    goto done;
-
-  if (!gst_amc_jni_call_object_method (env, err, format->object,
-          media_format.get_string, &v_str, key_str))
-    goto done;
-
-  *value = gst_amc_jni_string_to_gchar (env, v_str, TRUE);
-
-  ret = TRUE;
-
-done:
-  if (key_str)
-    gst_amc_jni_object_local_unref (env, key_str);
-
-  return ret;
-}
-
-gboolean
-gst_amc_format_set_string (GstAmcFormat * format, const gchar * key,
-    const gchar * value, GError ** err)
-{
-  JNIEnv *env;
-  jstring key_str = NULL;
-  jstring v_str = NULL;
-  gboolean ret = FALSE;
-
-  g_return_val_if_fail (format != NULL, FALSE);
-  g_return_val_if_fail (key != NULL, FALSE);
-  g_return_val_if_fail (value != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
-  if (!key_str)
-    goto done;
-
-  v_str = gst_amc_jni_string_from_gchar (env, err, FALSE, value);
-  if (!v_str)
-    goto done;
-
-  if (!gst_amc_jni_call_void_method (env, err, format->object,
-          media_format.set_string, key_str, v_str))
-    goto done;
-
-  ret = TRUE;
-
-done:
-  if (key_str)
-    gst_amc_jni_object_local_unref (env, key_str);
-  if (v_str)
-    gst_amc_jni_object_local_unref (env, v_str);
-
-  return ret;
-}
-
-gboolean
-gst_amc_format_get_buffer (GstAmcFormat * format, const gchar * key,
-    guint8 ** data, gsize * size, GError ** err)
-{
-  JNIEnv *env;
-  gboolean ret = FALSE;
-  jstring key_str = NULL;
-  jobject v = NULL;
-  GstAmcBuffer buf = { 0, };
-  gint position = 0, limit = 0;
-
-  g_return_val_if_fail (format != NULL, FALSE);
-  g_return_val_if_fail (key != NULL, FALSE);
-  g_return_val_if_fail (data != NULL, FALSE);
-  g_return_val_if_fail (size != NULL, FALSE);
-
-  *data = NULL;
-  *size = 0;
-  env = gst_amc_jni_get_env ();
-
-  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
-  if (!key_str)
-    goto done;
-
-  if (!gst_amc_jni_call_object_method (env, err, format->object,
-          media_format.get_byte_buffer, &v, key_str))
-    goto done;
-
-  *data = (*env)->GetDirectBufferAddress (env, v);
-  if (*data == NULL) {
-    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
-        GST_LIBRARY_ERROR_FAILED, "Failed get buffer address");
-    goto done;
-  }
-  *size = (*env)->GetDirectBufferCapacity (env, v);
-
-  buf.object = v;
-  buf.data = *data;
-  buf.size = *size;
-  gst_amc_buffer_get_position_and_limit (&buf, NULL, &position, &limit);
-  *size = limit;
-
-  *data = g_memdup (*data + position, limit);
-
-  ret = TRUE;
-
-done:
-  if (key_str)
-    gst_amc_jni_object_local_unref (env, key_str);
-  if (v)
-    gst_amc_jni_object_local_unref (env, v);
-
-  return ret;
-}
-
-gboolean
-gst_amc_format_set_buffer (GstAmcFormat * format, const gchar * key,
-    guint8 * data, gsize size, GError ** err)
-{
-  JNIEnv *env;
-  jstring key_str = NULL;
-  jobject v = NULL;
-  gboolean ret = FALSE;
-  GstAmcBuffer buf = { 0, };
-
-  g_return_val_if_fail (format != NULL, FALSE);
-  g_return_val_if_fail (key != NULL, FALSE);
-  g_return_val_if_fail (data != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
-  if (!key_str)
-    goto done;
-
-  /* FIXME: The memory must remain valid until the codec is stopped */
-  v = (*env)->NewDirectByteBuffer (env, data, size);
-  if (!v) {
-    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
-        GST_LIBRARY_ERROR_FAILED, "Failed create Java byte buffer");
-    goto done;
-  }
-
-  buf.object = v;
-  buf.data = data;
-  buf.size = size;
-
-  gst_amc_buffer_set_position_and_limit (&buf, NULL, 0, size);
-
-  if (!gst_amc_jni_call_void_method (env, err, format->object,
-          media_format.set_byte_buffer, key_str, v))
-    goto done;
-
-  ret = TRUE;
-
-done:
-  if (key_str)
-    gst_amc_jni_object_local_unref (env, key_str);
-  if (v)
-    gst_amc_jni_object_local_unref (env, v);
-
-  return ret;
-}
-
-static gboolean
-get_java_classes (void)
-{
-  gboolean ret = TRUE;
-  JNIEnv *env;
-  jclass tmp;
-
-  GST_DEBUG ("Retrieving Java classes");
-
-  env = gst_amc_jni_get_env ();
-
-  tmp = (*env)->FindClass (env, "java/lang/String");
-  if (!tmp) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get string class");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-  java_string.klass = (*env)->NewGlobalRef (env, tmp);
-  if (!java_string.klass) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get string class global reference");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-  (*env)->DeleteLocalRef (env, tmp);
-  tmp = NULL;
-
-  java_string.constructor =
-      (*env)->GetMethodID (env, java_string.klass, "<init>", "([C)V");
-  if (!java_string.constructor) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get string methods");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-
-  tmp = (*env)->FindClass (env, "android/media/MediaCodec");
-  if (!tmp) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get codec class");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-  media_codec.klass = (*env)->NewGlobalRef (env, tmp);
-  if (!media_codec.klass) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get codec class global reference");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-  (*env)->DeleteLocalRef (env, tmp);
-  tmp = NULL;
-
-  media_codec.create_by_codec_name =
-      (*env)->GetStaticMethodID (env, media_codec.klass, "createByCodecName",
-      "(Ljava/lang/String;)Landroid/media/MediaCodec;");
-  media_codec.configure =
-      (*env)->GetMethodID (env, media_codec.klass, "configure",
-      "(Landroid/media/MediaFormat;Landroid/view/Surface;Landroid/media/MediaCrypto;I)V");
-  media_codec.dequeue_input_buffer =
-      (*env)->GetMethodID (env, media_codec.klass, "dequeueInputBuffer",
-      "(J)I");
-  media_codec.dequeue_output_buffer =
-      (*env)->GetMethodID (env, media_codec.klass, "dequeueOutputBuffer",
-      "(Landroid/media/MediaCodec$BufferInfo;J)I");
-  media_codec.flush =
-      (*env)->GetMethodID (env, media_codec.klass, "flush", "()V");
-  media_codec.get_input_buffers =
-      (*env)->GetMethodID (env, media_codec.klass, "getInputBuffers",
-      "()[Ljava/nio/ByteBuffer;");
-  media_codec.get_output_buffers =
-      (*env)->GetMethodID (env, media_codec.klass, "getOutputBuffers",
-      "()[Ljava/nio/ByteBuffer;");
-  media_codec.get_output_format =
-      (*env)->GetMethodID (env, media_codec.klass, "getOutputFormat",
-      "()Landroid/media/MediaFormat;");
-  media_codec.queue_input_buffer =
-      (*env)->GetMethodID (env, media_codec.klass, "queueInputBuffer",
-      "(IIIJI)V");
-  media_codec.release =
-      (*env)->GetMethodID (env, media_codec.klass, "release", "()V");
-  media_codec.release_output_buffer =
-      (*env)->GetMethodID (env, media_codec.klass, "releaseOutputBuffer",
-      "(IZ)V");
-  media_codec.start =
-      (*env)->GetMethodID (env, media_codec.klass, "start", "()V");
-  media_codec.stop =
-      (*env)->GetMethodID (env, media_codec.klass, "stop", "()V");
-
-  if (!media_codec.configure ||
-      !media_codec.create_by_codec_name ||
-      !media_codec.dequeue_input_buffer ||
-      !media_codec.dequeue_output_buffer ||
-      !media_codec.flush ||
-      !media_codec.get_input_buffers ||
-      !media_codec.get_output_buffers ||
-      !media_codec.get_output_format ||
-      !media_codec.queue_input_buffer ||
-      !media_codec.release ||
-      !media_codec.release_output_buffer ||
-      !media_codec.start || !media_codec.stop) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get codec methods");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-
-  /* Android >= 21 */
-  media_codec.get_output_buffer =
-      (*env)->GetMethodID (env, media_codec.klass, "getOutputBuffer",
-      "(I)Ljava/nio/ByteBuffer;");
-  if ((*env)->ExceptionCheck (env))
-    (*env)->ExceptionClear (env);
-
-  /* Android >= 21 */
-  media_codec.get_input_buffer =
-      (*env)->GetMethodID (env, media_codec.klass, "getInputBuffer",
-      "(I)Ljava/nio/ByteBuffer;");
-  if ((*env)->ExceptionCheck (env))
-    (*env)->ExceptionClear (env);
-
-  tmp = (*env)->FindClass (env, "android/media/MediaCodec$BufferInfo");
-  if (!tmp) {
-    ret = FALSE;
-    (*env)->ExceptionClear (env);
-    GST_ERROR ("Failed to get codec buffer info class");
-    goto done;
-  }
-  media_codec_buffer_info.klass = (*env)->NewGlobalRef (env, tmp);
-  if (!media_codec_buffer_info.klass) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get codec buffer info class global reference");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-  (*env)->DeleteLocalRef (env, tmp);
-  tmp = NULL;
-
-  media_codec_buffer_info.constructor =
-      (*env)->GetMethodID (env, media_codec_buffer_info.klass, "<init>", "()V");
-  media_codec_buffer_info.flags =
-      (*env)->GetFieldID (env, media_codec_buffer_info.klass, "flags", "I");
-  media_codec_buffer_info.offset =
-      (*env)->GetFieldID (env, media_codec_buffer_info.klass, "offset", "I");
-  media_codec_buffer_info.presentation_time_us =
-      (*env)->GetFieldID (env, media_codec_buffer_info.klass,
-      "presentationTimeUs", "J");
-  media_codec_buffer_info.size =
-      (*env)->GetFieldID (env, media_codec_buffer_info.klass, "size", "I");
-  if (!media_codec_buffer_info.constructor || !media_codec_buffer_info.flags
-      || !media_codec_buffer_info.offset
-      || !media_codec_buffer_info.presentation_time_us
-      || !media_codec_buffer_info.size) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get buffer info methods and fields");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-
-  tmp = (*env)->FindClass (env, "android/media/MediaFormat");
-  if (!tmp) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get format class");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-  media_format.klass = (*env)->NewGlobalRef (env, tmp);
-  if (!media_format.klass) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get format class global reference");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-  (*env)->DeleteLocalRef (env, tmp);
-  tmp = NULL;
-
-  media_format.create_audio_format =
-      (*env)->GetStaticMethodID (env, media_format.klass, "createAudioFormat",
-      "(Ljava/lang/String;II)Landroid/media/MediaFormat;");
-  media_format.create_video_format =
-      (*env)->GetStaticMethodID (env, media_format.klass, "createVideoFormat",
-      "(Ljava/lang/String;II)Landroid/media/MediaFormat;");
-  media_format.to_string =
-      (*env)->GetMethodID (env, media_format.klass, "toString",
-      "()Ljava/lang/String;");
-  media_format.contains_key =
-      (*env)->GetMethodID (env, media_format.klass, "containsKey",
-      "(Ljava/lang/String;)Z");
-  media_format.get_float =
-      (*env)->GetMethodID (env, media_format.klass, "getFloat",
-      "(Ljava/lang/String;)F");
-  media_format.set_float =
-      (*env)->GetMethodID (env, media_format.klass, "setFloat",
-      "(Ljava/lang/String;F)V");
-  media_format.get_integer =
-      (*env)->GetMethodID (env, media_format.klass, "getInteger",
-      "(Ljava/lang/String;)I");
-  media_format.set_integer =
-      (*env)->GetMethodID (env, media_format.klass, "setInteger",
-      "(Ljava/lang/String;I)V");
-  media_format.get_string =
-      (*env)->GetMethodID (env, media_format.klass, "getString",
-      "(Ljava/lang/String;)Ljava/lang/String;");
-  media_format.set_string =
-      (*env)->GetMethodID (env, media_format.klass, "setString",
-      "(Ljava/lang/String;Ljava/lang/String;)V");
-  media_format.get_byte_buffer =
-      (*env)->GetMethodID (env, media_format.klass, "getByteBuffer",
-      "(Ljava/lang/String;)Ljava/nio/ByteBuffer;");
-  media_format.set_byte_buffer =
-      (*env)->GetMethodID (env, media_format.klass, "setByteBuffer",
-      "(Ljava/lang/String;Ljava/nio/ByteBuffer;)V");
-  if (!media_format.create_audio_format || !media_format.create_video_format
-      || !media_format.contains_key || !media_format.get_float
-      || !media_format.set_float || !media_format.get_integer
-      || !media_format.set_integer || !media_format.get_string
-      || !media_format.set_string || !media_format.get_byte_buffer
-      || !media_format.set_byte_buffer) {
-    ret = FALSE;
-    GST_ERROR ("Failed to get format methods");
-    if ((*env)->ExceptionCheck (env)) {
-      (*env)->ExceptionDescribe (env);
-      (*env)->ExceptionClear (env);
-    }
-    goto done;
-  }
-
-done:
-  if (tmp)
-    (*env)->DeleteLocalRef (env, tmp);
-  tmp = NULL;
-
-  return ret;
-}
-
 static gboolean
 scan_codecs (GstPlugin * plugin)
 {
@@ -3340,7 +2084,10 @@ amc_init (GstPlugin * plugin)
 
   gst_amc_codec_info_quark = g_quark_from_static_string ("gst-amc-codec-info");
 
-  if (!get_java_classes ())
+  if (!gst_amc_codec_static_init ())
+    return FALSE;
+
+  if (!gst_amc_format_static_init ())
     return FALSE;
 
   /* Set this to TRUE to allow registering decoders that have
index 383eb42..3700322 100644 (file)
 #include <jni.h>
 
 #include "gstjniutils.h"
+#include "gstamc-codec.h"
+#include "gstamc-format.h"
 
 G_BEGIN_DECLS
 
-typedef struct _GstAmcCodecInfo GstAmcCodecInfo;
 typedef struct _GstAmcCodecType GstAmcCodecType;
-typedef struct _GstAmcCodec GstAmcCodec;
-typedef struct _GstAmcBufferInfo GstAmcBufferInfo;
-typedef struct _GstAmcFormat GstAmcFormat;
-typedef struct _GstAmcColorFormatInfo GstAmcColorFormatInfo;
+typedef struct _GstAmcCodecInfo GstAmcCodecInfo;
 
 struct _GstAmcCodecType {
   gchar *mime;
@@ -58,66 +56,9 @@ struct _GstAmcCodecInfo {
   gint n_supported_types;
 };
 
-struct _GstAmcFormat {
-  /* < private > */
-  jobject object; /* global reference */
-};
-
-struct _GstAmcCodec {
-  /* < private > */
-  jobject object; /* global reference */
-
-  GstAmcBuffer *input_buffers, *output_buffers;
-  gsize n_input_buffers, n_output_buffers;
-};
-
-struct _GstAmcBufferInfo {
-  gint flags;
-  gint offset;
-  gint64 presentation_time_us;
-  gint size;
-};
 
 extern GQuark gst_amc_codec_info_quark;
 
-GstAmcCodec * gst_amc_codec_new (const gchar *name, GError **err);
-void gst_amc_codec_free (GstAmcCodec * codec);
-
-gboolean gst_amc_codec_configure (GstAmcCodec * codec, GstAmcFormat * format, jobject surface, gint flags, GError **err);
-GstAmcFormat * gst_amc_codec_get_output_format (GstAmcCodec * codec, GError **err);
-
-gboolean gst_amc_codec_start (GstAmcCodec * codec, GError **err);
-gboolean gst_amc_codec_stop (GstAmcCodec * codec, GError **err);
-gboolean gst_amc_codec_flush (GstAmcCodec * codec, GError **err);
-gboolean gst_amc_codec_release (GstAmcCodec * codec, GError **err);
-
-GstAmcBuffer * gst_amc_codec_get_output_buffer (GstAmcCodec * codec, gint index, GError **err);
-GstAmcBuffer * gst_amc_codec_get_input_buffer (GstAmcCodec * codec, gint index, GError **err);
-
-gint gst_amc_codec_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs, GError **err);
-gint gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec, GstAmcBufferInfo *info, gint64 timeoutUs, GError **err);
-
-gboolean gst_amc_codec_queue_input_buffer (GstAmcCodec * codec, gint index, const GstAmcBufferInfo *info, GError **err);
-gboolean gst_amc_codec_release_output_buffer (GstAmcCodec * codec, gint index, gboolean render, GError **err);
-
-
-GstAmcFormat * gst_amc_format_new_audio (const gchar *mime, gint sample_rate, gint channels, GError **err);
-GstAmcFormat * gst_amc_format_new_video (const gchar *mime, gint width, gint height, GError **err);
-void gst_amc_format_free (GstAmcFormat * format);
-
-gchar * gst_amc_format_to_string (GstAmcFormat * format, GError **err);
-
-gboolean gst_amc_format_contains_key (GstAmcFormat *format, const gchar *key, GError **err);
-
-gboolean gst_amc_format_get_float (GstAmcFormat *format, const gchar *key, gfloat *value, GError **err);
-gboolean gst_amc_format_set_float (GstAmcFormat *format, const gchar *key, gfloat value, GError **err);
-gboolean gst_amc_format_get_int (GstAmcFormat *format, const gchar *key, gint *value, GError **err);
-gboolean gst_amc_format_set_int (GstAmcFormat *format, const gchar *key, gint value, GError **err);
-gboolean gst_amc_format_get_string (GstAmcFormat *format, const gchar *key, gchar **value, GError **err);
-gboolean gst_amc_format_set_string (GstAmcFormat *format, const gchar *key, const gchar *value, GError **err);
-gboolean gst_amc_format_get_buffer (GstAmcFormat *format, const gchar *key, guint8 **data, gsize *size, GError **err);
-gboolean gst_amc_format_set_buffer (GstAmcFormat *format, const gchar *key, guint8 *data, gsize size, GError **err);
-
 GstVideoFormat gst_amc_color_format_to_video_format (const GstAmcCodecInfo * codec_info, const gchar * mime, gint color_format);
 gint gst_amc_video_format_to_color_format (const GstAmcCodecInfo * codec_info, const gchar * mime, GstVideoFormat video_format);
 
index 0f2efc2..d7c9da4 100644 (file)
@@ -43,14 +43,6 @@ static gboolean started_java_vm = FALSE;
 static pthread_key_t current_jni_env;
 static jobject (*get_class_loader) (void);
 
-static struct
-{
-  jclass klass;
-  jmethodID get_limit, get_position;
-  jmethodID set_limit, set_position;
-  jmethodID clear;
-} java_nio_buffer;
-
 jclass
 gst_amc_jni_get_class (JNIEnv * env, GError ** err, const gchar * name)
 {
@@ -610,63 +602,6 @@ check_application_class_loader (void)
 static gboolean
 initialize_classes (void)
 {
-  JNIEnv *env;
-  GError *err = NULL;
-
-  env = gst_amc_jni_get_env ();
-
-  java_nio_buffer.klass = gst_amc_jni_get_class (env, &err, "java/nio/Buffer");
-  if (!java_nio_buffer.klass) {
-    GST_ERROR ("Failed to get java.nio.Buffer class: %s", err->message);
-    g_clear_error (&err);
-    return FALSE;
-  }
-
-  java_nio_buffer.get_limit =
-      gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "limit",
-      "()I");
-  if (!java_nio_buffer.get_limit) {
-    GST_ERROR ("Failed to get java.nio.Buffer limit(): %s", err->message);
-    g_clear_error (&err);
-    return FALSE;
-  }
-
-  java_nio_buffer.get_position =
-      gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "position",
-      "()I");
-  if (!java_nio_buffer.get_position) {
-    GST_ERROR ("Failed to get java.nio.Buffer position(): %s", err->message);
-    g_clear_error (&err);
-    return FALSE;
-  }
-
-  java_nio_buffer.set_limit =
-      gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "limit",
-      "(I)Ljava/nio/Buffer;");
-  if (!java_nio_buffer.set_limit) {
-    GST_ERROR ("Failed to get java.nio.Buffer limit(): %s", err->message);
-    g_clear_error (&err);
-    return FALSE;
-  }
-
-  java_nio_buffer.set_position =
-      gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "position",
-      "(I)Ljava/nio/Buffer;");
-  if (!java_nio_buffer.set_position) {
-    GST_ERROR ("Failed to get java.nio.Buffer position(): %s", err->message);
-    g_clear_error (&err);
-    return FALSE;
-  }
-
-  java_nio_buffer.clear =
-      gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "clear",
-      "()Ljava/nio/Buffer;");
-  if (!java_nio_buffer.clear) {
-    GST_ERROR ("Failed to get java.nio.Buffer clear(): %s", err->message);
-    g_clear_error (&err);
-    return FALSE;
-  }
-
   if (!check_application_class_loader ()) {
     GST_ERROR ("Could not find application class loader provider");
     return FALSE;
@@ -1050,173 +985,3 @@ GET_STATIC_TYPE_FIELD (gint64, long, Long);
 GET_STATIC_TYPE_FIELD (gfloat, float, Float);
 GET_STATIC_TYPE_FIELD (gdouble, double, Double);
 GET_STATIC_TYPE_FIELD (jobject, object, Object);
-
-gboolean
-gst_amc_jni_get_buffer_array (JNIEnv * env, GError ** err, jobject array,
-    GstAmcBuffer ** buffers, gsize * n_buffers)
-{
-  jsize i;
-
-  *n_buffers = (*env)->GetArrayLength (env, array);
-  *buffers = g_new0 (GstAmcBuffer, *n_buffers);
-
-  for (i = 0; i < *n_buffers; i++) {
-    jobject buffer = NULL;
-
-    buffer = (*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 buffer %d", i);
-      goto error;
-    }
-
-    /* NULL buffers are not a problem and are happening when we configured
-     * a surface as input/output */
-    if (!buffer)
-      continue;
-
-    (*buffers)[i].object = gst_amc_jni_object_make_global (env, buffer);
-    if (!(*buffers)[i].object) {
-      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
-          GST_LIBRARY_ERROR_FAILED,
-          "Failed to create global buffer reference %d", i);
-      goto error;
-    }
-
-    (*buffers)[i].data =
-        (*env)->GetDirectBufferAddress (env, (*buffers)[i].object);
-    if (!(*buffers)[i].data) {
-      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
-          GST_LIBRARY_ERROR_FAILED, "Failed to get buffer address %d", i);
-      goto error;
-    }
-    (*buffers)[i].size =
-        (*env)->GetDirectBufferCapacity (env, (*buffers)[i].object);
-  }
-
-  return TRUE;
-
-error:
-  if (*buffers)
-    gst_amc_jni_free_buffer_array (env, *buffers, *n_buffers);
-  *buffers = NULL;
-  *n_buffers = 0;
-  return FALSE;
-}
-
-void
-gst_amc_jni_free_buffer_array (JNIEnv * env, GstAmcBuffer * buffers,
-    gsize n_buffers)
-{
-  jsize i;
-
-  g_return_if_fail (buffers != NULL);
-
-  for (i = 0; i < n_buffers; i++) {
-    if (buffers[i].object)
-      gst_amc_jni_object_unref (env, buffers[i].object);
-  }
-  g_free (buffers);
-}
-
-void
-gst_amc_buffer_free (GstAmcBuffer * buffer)
-{
-  JNIEnv *env;
-
-  g_return_if_fail (buffer != NULL);
-
-  env = gst_amc_jni_get_env ();
-
-  if (buffer->object)
-    gst_amc_jni_object_unref (env, buffer->object);
-  g_free (buffer);
-}
-
-GstAmcBuffer *
-gst_amc_buffer_copy (GstAmcBuffer * buffer)
-{
-  JNIEnv *env;
-  GstAmcBuffer *ret;
-
-  g_return_val_if_fail (buffer != NULL, NULL);
-
-  env = gst_amc_jni_get_env ();
-
-  ret = g_new0 (GstAmcBuffer, 1);
-
-  ret->object = gst_amc_jni_object_ref (env, buffer->object);
-  ret->data = buffer->data;
-  ret->size = buffer->size;
-
-  return ret;
-}
-
-gboolean
-gst_amc_buffer_get_position_and_limit (GstAmcBuffer * buffer, GError ** err,
-    gint * position, gint * limit)
-{
-  JNIEnv *env;
-
-  g_return_val_if_fail (buffer != NULL, FALSE);
-  g_return_val_if_fail (buffer->object != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  if (!gst_amc_jni_call_int_method (env, err, buffer->object,
-          java_nio_buffer.get_position, position))
-    return FALSE;
-
-  if (!gst_amc_jni_call_int_method (env, err, buffer->object,
-          java_nio_buffer.get_limit, limit))
-    return FALSE;
-
-  return TRUE;
-}
-
-gboolean
-gst_amc_buffer_set_position_and_limit (GstAmcBuffer * buffer, GError ** err,
-    gint position, gint limit)
-{
-  JNIEnv *env;
-  jobject tmp;
-
-  g_return_val_if_fail (buffer != NULL, FALSE);
-  g_return_val_if_fail (buffer->object != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  if (!gst_amc_jni_call_object_method (env, err, buffer->object,
-          java_nio_buffer.set_limit, &tmp, limit))
-    return FALSE;
-
-  gst_amc_jni_object_local_unref (env, tmp);
-
-  if (!gst_amc_jni_call_object_method (env, err, buffer->object,
-          java_nio_buffer.set_position, &tmp, position))
-    return FALSE;
-
-  gst_amc_jni_object_local_unref (env, tmp);
-
-  return TRUE;
-}
-
-gboolean
-gst_amc_buffer_clear (GstAmcBuffer * buffer, GError ** err)
-{
-  JNIEnv *env;
-  jobject tmp;
-
-  g_return_val_if_fail (buffer != NULL, FALSE);
-  g_return_val_if_fail (buffer->object != NULL, FALSE);
-
-  env = gst_amc_jni_get_env ();
-
-  if (!gst_amc_jni_call_object_method (env, err, buffer->object,
-          java_nio_buffer.clear, &tmp))
-    return FALSE;
-
-  gst_amc_jni_object_local_unref (env, tmp);
-
-  return TRUE;
-}
index c323cf8..1d30edc 100644 (file)
@@ -173,21 +173,4 @@ DEF_GET_STATIC_TYPE_FIELD (gfloat, float, Float);
 DEF_GET_STATIC_TYPE_FIELD (gdouble, double, Double);
 DEF_GET_STATIC_TYPE_FIELD (jobject, object, Object);
 
-typedef struct _GstAmcBuffer GstAmcBuffer;
-
-struct _GstAmcBuffer {
-  jobject object; /* global reference */
-  guint8 *data;
-  gsize size;
-};
-
-gboolean gst_amc_buffer_get_position_and_limit (GstAmcBuffer * buffer, GError ** err, gint * position, gint * limit);
-gboolean gst_amc_buffer_set_position_and_limit (GstAmcBuffer * buffer, GError ** err, gint position, gint limit);
-gboolean gst_amc_buffer_clear (GstAmcBuffer * buffer, GError ** err);
-GstAmcBuffer * gst_amc_buffer_copy (GstAmcBuffer * buffer);
-void     gst_amc_buffer_free (GstAmcBuffer * buffer);
-
-gboolean gst_amc_jni_get_buffer_array (JNIEnv * env, GError ** err, jobject array, GstAmcBuffer ** buffers, gsize * n_buffers);
-void gst_amc_jni_free_buffer_array (JNIEnv * env, GstAmcBuffer * buffers, gsize n_buffers);
-
 #endif
diff --git a/sys/androidmedia/jni/gstamc-codec-jni.c b/sys/androidmedia/jni/gstamc-codec-jni.c
new file mode 100644 (file)
index 0000000..f6fa010
--- /dev/null
@@ -0,0 +1,936 @@
+/*
+ * 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-codec.h"
+#include "../gstamc-constants.h"
+#include "gstamc-internal-jni.h"
+
+struct _GstAmcCodec
+{
+  jobject object;               /* global reference */
+
+  RealBuffer *input_buffers, *output_buffers;
+  gsize n_input_buffers, n_output_buffers;
+};
+
+static struct
+{
+  jclass klass;
+  jmethodID configure;
+  jmethodID create_by_codec_name;
+  jmethodID dequeue_input_buffer;
+  jmethodID dequeue_output_buffer;
+  jmethodID flush;
+  jmethodID get_input_buffers;
+  jmethodID get_input_buffer;
+  jmethodID get_output_buffers;
+  jmethodID get_output_buffer;
+  jmethodID get_output_format;
+  jmethodID queue_input_buffer;
+  jmethodID release;
+  jmethodID release_output_buffer;
+  jmethodID start;
+  jmethodID stop;
+} media_codec;
+
+static struct
+{
+  jclass klass;
+  jmethodID constructor;
+  jfieldID flags;
+  jfieldID offset;
+  jfieldID presentation_time_us;
+  jfieldID size;
+} media_codec_buffer_info;
+
+static struct
+{
+  jclass klass;
+  jmethodID get_limit, get_position;
+  jmethodID set_limit, set_position;
+  jmethodID clear;
+} java_nio_buffer;
+
+gboolean
+gst_amc_codec_static_init (void)
+{
+  gboolean ret = TRUE;
+  JNIEnv *env;
+  jclass tmp;
+  GError *err = NULL;
+
+  env = gst_amc_jni_get_env ();
+
+  java_nio_buffer.klass = gst_amc_jni_get_class (env, &err, "java/nio/Buffer");
+  if (!java_nio_buffer.klass) {
+    GST_ERROR ("Failed to get java.nio.Buffer class: %s", err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  java_nio_buffer.get_limit =
+      gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "limit",
+      "()I");
+  if (!java_nio_buffer.get_limit) {
+    GST_ERROR ("Failed to get java.nio.Buffer limit(): %s", err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  java_nio_buffer.get_position =
+      gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "position",
+      "()I");
+  if (!java_nio_buffer.get_position) {
+    GST_ERROR ("Failed to get java.nio.Buffer position(): %s", err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  java_nio_buffer.set_limit =
+      gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "limit",
+      "(I)Ljava/nio/Buffer;");
+  if (!java_nio_buffer.set_limit) {
+    GST_ERROR ("Failed to get java.nio.Buffer limit(): %s", err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  java_nio_buffer.set_position =
+      gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "position",
+      "(I)Ljava/nio/Buffer;");
+  if (!java_nio_buffer.set_position) {
+    GST_ERROR ("Failed to get java.nio.Buffer position(): %s", err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  java_nio_buffer.clear =
+      gst_amc_jni_get_method_id (env, &err, java_nio_buffer.klass, "clear",
+      "()Ljava/nio/Buffer;");
+  if (!java_nio_buffer.clear) {
+    GST_ERROR ("Failed to get java.nio.Buffer clear(): %s", err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  tmp = (*env)->FindClass (env, "android/media/MediaCodec$BufferInfo");
+  if (!tmp) {
+    ret = FALSE;
+    (*env)->ExceptionClear (env);
+    GST_ERROR ("Failed to get codec buffer info class");
+    goto done;
+  }
+  media_codec_buffer_info.klass = (*env)->NewGlobalRef (env, tmp);
+  if (!media_codec_buffer_info.klass) {
+    ret = FALSE;
+    GST_ERROR ("Failed to get codec buffer info class global reference");
+    if ((*env)->ExceptionCheck (env)) {
+      (*env)->ExceptionDescribe (env);
+      (*env)->ExceptionClear (env);
+    }
+    goto done;
+  }
+  (*env)->DeleteLocalRef (env, tmp);
+  tmp = NULL;
+
+  media_codec_buffer_info.constructor =
+      (*env)->GetMethodID (env, media_codec_buffer_info.klass, "<init>", "()V");
+  media_codec_buffer_info.flags =
+      (*env)->GetFieldID (env, media_codec_buffer_info.klass, "flags", "I");
+  media_codec_buffer_info.offset =
+      (*env)->GetFieldID (env, media_codec_buffer_info.klass, "offset", "I");
+  media_codec_buffer_info.presentation_time_us =
+      (*env)->GetFieldID (env, media_codec_buffer_info.klass,
+      "presentationTimeUs", "J");
+  media_codec_buffer_info.size =
+      (*env)->GetFieldID (env, media_codec_buffer_info.klass, "size", "I");
+  if (!media_codec_buffer_info.constructor || !media_codec_buffer_info.flags
+      || !media_codec_buffer_info.offset
+      || !media_codec_buffer_info.presentation_time_us
+      || !media_codec_buffer_info.size) {
+    ret = FALSE;
+    GST_ERROR ("Failed to get buffer info methods and fields");
+    if ((*env)->ExceptionCheck (env)) {
+      (*env)->ExceptionDescribe (env);
+      (*env)->ExceptionClear (env);
+    }
+    goto done;
+  }
+
+  tmp = (*env)->FindClass (env, "android/media/MediaCodec");
+  if (!tmp) {
+    ret = FALSE;
+    GST_ERROR ("Failed to get codec class");
+    if ((*env)->ExceptionCheck (env)) {
+      (*env)->ExceptionDescribe (env);
+      (*env)->ExceptionClear (env);
+    }
+    goto done;
+  }
+  media_codec.klass = (*env)->NewGlobalRef (env, tmp);
+  if (!media_codec.klass) {
+    ret = FALSE;
+    GST_ERROR ("Failed to get codec class global reference");
+    if ((*env)->ExceptionCheck (env)) {
+      (*env)->ExceptionDescribe (env);
+      (*env)->ExceptionClear (env);
+    }
+    goto done;
+  }
+  (*env)->DeleteLocalRef (env, tmp);
+  tmp = NULL;
+
+  media_codec.create_by_codec_name =
+      (*env)->GetStaticMethodID (env, media_codec.klass, "createByCodecName",
+      "(Ljava/lang/String;)Landroid/media/MediaCodec;");
+  media_codec.configure =
+      (*env)->GetMethodID (env, media_codec.klass, "configure",
+      "(Landroid/media/MediaFormat;Landroid/view/Surface;Landroid/media/MediaCrypto;I)V");
+  media_codec.dequeue_input_buffer =
+      (*env)->GetMethodID (env, media_codec.klass, "dequeueInputBuffer",
+      "(J)I");
+  media_codec.dequeue_output_buffer =
+      (*env)->GetMethodID (env, media_codec.klass, "dequeueOutputBuffer",
+      "(Landroid/media/MediaCodec$BufferInfo;J)I");
+  media_codec.flush =
+      (*env)->GetMethodID (env, media_codec.klass, "flush", "()V");
+  media_codec.get_input_buffers =
+      (*env)->GetMethodID (env, media_codec.klass, "getInputBuffers",
+      "()[Ljava/nio/ByteBuffer;");
+  media_codec.get_output_buffers =
+      (*env)->GetMethodID (env, media_codec.klass, "getOutputBuffers",
+      "()[Ljava/nio/ByteBuffer;");
+  media_codec.get_output_format =
+      (*env)->GetMethodID (env, media_codec.klass, "getOutputFormat",
+      "()Landroid/media/MediaFormat;");
+  media_codec.queue_input_buffer =
+      (*env)->GetMethodID (env, media_codec.klass, "queueInputBuffer",
+      "(IIIJI)V");
+  media_codec.release =
+      (*env)->GetMethodID (env, media_codec.klass, "release", "()V");
+  media_codec.release_output_buffer =
+      (*env)->GetMethodID (env, media_codec.klass, "releaseOutputBuffer",
+      "(IZ)V");
+  media_codec.start =
+      (*env)->GetMethodID (env, media_codec.klass, "start", "()V");
+  media_codec.stop =
+      (*env)->GetMethodID (env, media_codec.klass, "stop", "()V");
+
+  if (!media_codec.configure ||
+      !media_codec.create_by_codec_name ||
+      !media_codec.dequeue_input_buffer ||
+      !media_codec.dequeue_output_buffer ||
+      !media_codec.flush ||
+      !media_codec.get_input_buffers ||
+      !media_codec.get_output_buffers ||
+      !media_codec.get_output_format ||
+      !media_codec.queue_input_buffer ||
+      !media_codec.release ||
+      !media_codec.release_output_buffer ||
+      !media_codec.start || !media_codec.stop) {
+    ret = FALSE;
+    GST_ERROR ("Failed to get codec methods");
+    if ((*env)->ExceptionCheck (env)) {
+      (*env)->ExceptionDescribe (env);
+      (*env)->ExceptionClear (env);
+    }
+    goto done;
+  }
+
+  /* Android >= 21 */
+  media_codec.get_output_buffer =
+      (*env)->GetMethodID (env, media_codec.klass, "getOutputBuffer",
+      "(I)Ljava/nio/ByteBuffer;");
+  if ((*env)->ExceptionCheck (env))
+    (*env)->ExceptionClear (env);
+
+  /* Android >= 21 */
+  media_codec.get_input_buffer =
+      (*env)->GetMethodID (env, media_codec.klass, "getInputBuffer",
+      "(I)Ljava/nio/ByteBuffer;");
+  if ((*env)->ExceptionCheck (env))
+    (*env)->ExceptionClear (env);
+
+done:
+  if (tmp)
+    (*env)->DeleteLocalRef (env, tmp);
+  tmp = NULL;
+
+  return ret;
+}
+
+static void
+gst_amc_jni_free_buffer_array (JNIEnv * env, RealBuffer * buffers,
+    gsize n_buffers)
+{
+  jsize i;
+
+  g_return_if_fail (buffers != NULL);
+
+  for (i = 0; i < n_buffers; i++) {
+    if (buffers[i].object)
+      gst_amc_jni_object_unref (env, buffers[i].object);
+  }
+  g_free (buffers);
+}
+
+static gboolean
+gst_amc_jni_get_buffer_array (JNIEnv * env, GError ** err, jobject array,
+    RealBuffer ** buffers_, gsize * n_buffers)
+{
+  RealBuffer **buffers = (RealBuffer **) buffers_;
+  jsize i;
+
+  *n_buffers = (*env)->GetArrayLength (env, array);
+  *buffers = g_new0 (RealBuffer, *n_buffers);
+
+  for (i = 0; i < *n_buffers; i++) {
+    jobject buffer = NULL;
+
+    buffer = (*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 buffer %d", i);
+      goto error;
+    }
+
+    /* NULL buffers are not a problem and are happening when we configured
+     * a surface as input/output */
+    if (!buffer)
+      continue;
+
+    (*buffers)[i].object = gst_amc_jni_object_make_global (env, buffer);
+    if (!(*buffers)[i].object) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED,
+          "Failed to create global buffer reference %d", i);
+      goto error;
+    }
+
+    (*buffers)[i].data =
+        (*env)->GetDirectBufferAddress (env, (*buffers)[i].object);
+    if (!(*buffers)[i].data) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED, "Failed to get buffer address %d", i);
+      goto error;
+    }
+    (*buffers)[i].size =
+        (*env)->GetDirectBufferCapacity (env, (*buffers)[i].object);
+  }
+
+  return TRUE;
+
+error:
+  if (*buffers)
+    gst_amc_jni_free_buffer_array (env, *buffers, *n_buffers);
+  *buffers = NULL;
+  *n_buffers = 0;
+  return FALSE;
+}
+
+void
+gst_amc_buffer_free (GstAmcBuffer * buffer_)
+{
+  RealBuffer *buffer = (RealBuffer *) buffer_;
+  JNIEnv *env;
+
+  g_return_if_fail (buffer != NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  if (buffer->object)
+    gst_amc_jni_object_unref (env, buffer->object);
+  g_free (buffer);
+}
+
+static GstAmcBuffer *
+gst_amc_buffer_copy (RealBuffer * buffer)
+{
+  JNIEnv *env;
+  RealBuffer *ret;
+
+  g_return_val_if_fail (buffer != NULL, NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  ret = g_new0 (RealBuffer, 1);
+
+  ret->object = gst_amc_jni_object_ref (env, buffer->object);
+  ret->data = buffer->data;
+  ret->size = buffer->size;
+
+  return (GstAmcBuffer *) ret;
+}
+
+gboolean
+gst_amc_buffer_get_position_and_limit (RealBuffer * buffer_, GError ** err,
+    gint * position, gint * limit)
+{
+  RealBuffer *buffer = (RealBuffer *) buffer_;
+  JNIEnv *env;
+
+  g_return_val_if_fail (buffer != NULL, FALSE);
+  g_return_val_if_fail (buffer->object != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_int_method (env, err, buffer->object,
+          java_nio_buffer.get_position, position))
+    return FALSE;
+
+  if (!gst_amc_jni_call_int_method (env, err, buffer->object,
+          java_nio_buffer.get_limit, limit))
+    return FALSE;
+
+  return TRUE;
+}
+
+gboolean
+gst_amc_buffer_set_position_and_limit (GstAmcBuffer * buffer_, GError ** err,
+    gint position, gint limit)
+{
+  RealBuffer *buffer = (RealBuffer *) buffer_;
+  JNIEnv *env;
+  jobject tmp;
+
+  g_return_val_if_fail (buffer != NULL, FALSE);
+  g_return_val_if_fail (buffer->object != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_object_method (env, err, buffer->object,
+          java_nio_buffer.set_limit, &tmp, limit))
+    return FALSE;
+
+  gst_amc_jni_object_local_unref (env, tmp);
+
+  if (!gst_amc_jni_call_object_method (env, err, buffer->object,
+          java_nio_buffer.set_position, &tmp, position))
+    return FALSE;
+
+  gst_amc_jni_object_local_unref (env, tmp);
+
+  return TRUE;
+}
+
+GstAmcCodec *
+gst_amc_codec_new (const gchar * name, GError ** err)
+{
+  JNIEnv *env;
+  GstAmcCodec *codec = NULL;
+  jstring name_str;
+  jobject object = NULL;
+
+  g_return_val_if_fail (name != NULL, NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  name_str = gst_amc_jni_string_from_gchar (env, err, FALSE, name);
+  if (!name_str) {
+    goto error;
+  }
+
+  codec = g_slice_new0 (GstAmcCodec);
+
+  if (!gst_amc_jni_call_static_object_method (env, err, media_codec.klass,
+          media_codec.create_by_codec_name, &object, name_str))
+    goto error;
+
+  codec->object = gst_amc_jni_object_make_global (env, object);
+  object = NULL;
+
+  if (!codec->object) {
+    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+        GST_LIBRARY_ERROR_SETTINGS, "Failed to create global codec reference");
+    goto error;
+  }
+
+done:
+  if (name_str)
+    gst_amc_jni_object_local_unref (env, name_str);
+  name_str = NULL;
+
+  return codec;
+
+error:
+  if (codec)
+    g_slice_free (GstAmcCodec, codec);
+  codec = NULL;
+  goto done;
+}
+
+void
+gst_amc_codec_free (GstAmcCodec * codec)
+{
+  JNIEnv *env;
+
+  g_return_if_fail (codec != NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  if (codec->input_buffers)
+    gst_amc_jni_free_buffer_array (env, codec->input_buffers,
+        codec->n_input_buffers);
+  codec->input_buffers = NULL;
+  codec->n_input_buffers = 0;
+
+  if (codec->output_buffers)
+    gst_amc_jni_free_buffer_array (env, codec->output_buffers,
+        codec->n_output_buffers);
+  codec->output_buffers = NULL;
+  codec->n_output_buffers = 0;
+
+  gst_amc_jni_object_unref (env, codec->object);
+  g_slice_free (GstAmcCodec, codec);
+}
+
+gboolean
+gst_amc_codec_configure (GstAmcCodec * codec, GstAmcFormat * format,
+    jobject surface, gint flags, GError ** err)
+{
+  JNIEnv *env;
+
+  g_return_val_if_fail (codec != NULL, FALSE);
+  g_return_val_if_fail (format != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+  return gst_amc_jni_call_void_method (env, err, codec->object,
+      media_codec.configure, format->object, surface, NULL, flags);
+}
+
+GstAmcFormat *
+gst_amc_codec_get_output_format (GstAmcCodec * codec, GError ** err)
+{
+  JNIEnv *env;
+  GstAmcFormat *ret = NULL;
+  jobject object = NULL;
+
+  g_return_val_if_fail (codec != NULL, NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_object_method (env, err, codec->object,
+          media_codec.get_output_format, &object))
+    goto done;
+
+  ret = g_slice_new0 (GstAmcFormat);
+
+  ret->object = gst_amc_jni_object_make_global (env, object);
+  if (!ret->object) {
+    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+        GST_LIBRARY_ERROR_SETTINGS, "Failed to create global format reference");
+    g_slice_free (GstAmcFormat, ret);
+    ret = NULL;
+  }
+
+done:
+
+  return ret;
+}
+
+static RealBuffer *
+gst_amc_codec_get_input_buffers (GstAmcCodec * codec, gsize * n_buffers,
+    GError ** err)
+{
+  JNIEnv *env;
+  jobject input_buffers = NULL;
+  RealBuffer *ret = NULL;
+
+  g_return_val_if_fail (codec != NULL, NULL);
+  g_return_val_if_fail (n_buffers != NULL, NULL);
+
+  *n_buffers = 0;
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_object_method (env, err, codec->object,
+          media_codec.get_input_buffers, &input_buffers))
+    goto done;
+
+  gst_amc_jni_get_buffer_array (env, err, input_buffers, &ret, n_buffers);
+
+done:
+  if (input_buffers)
+    gst_amc_jni_object_local_unref (env, input_buffers);
+
+  return ret;
+}
+
+static RealBuffer *
+gst_amc_codec_get_output_buffers (GstAmcCodec * codec, gsize * n_buffers,
+    GError ** err)
+{
+  JNIEnv *env;
+  jobject output_buffers = NULL;
+  RealBuffer *ret = NULL;
+
+  g_return_val_if_fail (codec != NULL, NULL);
+  g_return_val_if_fail (n_buffers != NULL, NULL);
+
+  *n_buffers = 0;
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_object_method (env, err, codec->object,
+          media_codec.get_output_buffers, &output_buffers))
+    goto done;
+
+  gst_amc_jni_get_buffer_array (env, err, output_buffers, &ret, n_buffers);
+
+done:
+  if (output_buffers)
+    gst_amc_jni_object_local_unref (env, output_buffers);
+
+  return ret;
+}
+
+gboolean
+gst_amc_codec_start (GstAmcCodec * codec, GError ** err)
+{
+  JNIEnv *env;
+  gboolean ret;
+
+  g_return_val_if_fail (codec != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+  ret = gst_amc_jni_call_void_method (env, err, codec->object,
+      media_codec.start);
+  if (!ret)
+    return ret;
+
+  if (!media_codec.get_input_buffer) {
+    if (codec->input_buffers)
+      gst_amc_jni_free_buffer_array (env, codec->input_buffers,
+          codec->n_input_buffers);
+    codec->input_buffers =
+        gst_amc_codec_get_input_buffers (codec, &codec->n_input_buffers, err);
+    if (!codec->input_buffers) {
+      gst_amc_codec_stop (codec, NULL);
+      return FALSE;
+    }
+  }
+
+  return ret;
+}
+
+gboolean
+gst_amc_codec_stop (GstAmcCodec * codec, GError ** err)
+{
+  JNIEnv *env;
+
+  g_return_val_if_fail (codec != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  if (codec->input_buffers)
+    gst_amc_jni_free_buffer_array (env, codec->input_buffers,
+        codec->n_input_buffers);
+  codec->input_buffers = NULL;
+  codec->n_input_buffers = 0;
+
+  if (codec->output_buffers)
+    gst_amc_jni_free_buffer_array (env, codec->output_buffers,
+        codec->n_output_buffers);
+  codec->output_buffers = NULL;
+  codec->n_output_buffers = 0;
+
+  return gst_amc_jni_call_void_method (env, err, codec->object,
+      media_codec.stop);
+}
+
+gboolean
+gst_amc_codec_flush (GstAmcCodec * codec, GError ** err)
+{
+  JNIEnv *env;
+
+  g_return_val_if_fail (codec != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+  return gst_amc_jni_call_void_method (env, err, codec->object,
+      media_codec.flush);
+}
+
+gboolean
+gst_amc_codec_release (GstAmcCodec * codec, GError ** err)
+{
+  JNIEnv *env;
+
+  g_return_val_if_fail (codec != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  if (codec->input_buffers)
+    gst_amc_jni_free_buffer_array (env, codec->input_buffers,
+        codec->n_input_buffers);
+  codec->input_buffers = NULL;
+  codec->n_input_buffers = 0;
+
+  if (codec->output_buffers)
+    gst_amc_jni_free_buffer_array (env, codec->output_buffers,
+        codec->n_output_buffers);
+  codec->output_buffers = NULL;
+  codec->n_output_buffers = 0;
+
+  return gst_amc_jni_call_void_method (env, err, codec->object,
+      media_codec.release);
+}
+
+GstAmcBuffer *
+gst_amc_codec_get_output_buffer (GstAmcCodec * codec, gint index, GError ** err)
+{
+  JNIEnv *env;
+  jobject buffer = NULL;
+  RealBuffer *ret = NULL;
+
+  g_return_val_if_fail (codec != NULL, NULL);
+  g_return_val_if_fail (index >= 0, NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  if (!media_codec.get_output_buffer) {
+    g_return_val_if_fail (index < codec->n_output_buffers && index >= 0, NULL);
+    if (codec->output_buffers[index].object)
+      return gst_amc_buffer_copy (&codec->output_buffers[index]);
+    else
+      return NULL;
+  }
+
+  if (!gst_amc_jni_call_object_method (env, err, codec->object,
+          media_codec.get_output_buffer, &buffer, index))
+    goto done;
+
+  if (buffer != NULL) {
+    ret = g_new0 (RealBuffer, 1);
+    ret->object = gst_amc_jni_object_make_global (env, buffer);
+    if (!ret->object) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED, "Failed to create global buffer reference");
+      goto error;
+    }
+
+    ret->data = (*env)->GetDirectBufferAddress (env, ret->object);
+    if (!ret->data) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED, "Failed to get buffer address");
+      goto error;
+    }
+    ret->size = (*env)->GetDirectBufferCapacity (env, ret->object);
+  }
+
+done:
+
+  return (GstAmcBuffer *) ret;
+
+error:
+  if (ret->object)
+    gst_amc_jni_object_unref (env, ret->object);
+  g_free (ret);
+
+  return NULL;
+}
+
+GstAmcBuffer *
+gst_amc_codec_get_input_buffer (GstAmcCodec * codec, gint index, GError ** err)
+{
+  JNIEnv *env;
+  jobject buffer = NULL;
+  RealBuffer *ret = NULL;
+
+  g_return_val_if_fail (codec != NULL, NULL);
+  g_return_val_if_fail (index >= 0, NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  if (!media_codec.get_input_buffer) {
+    g_return_val_if_fail (index < codec->n_input_buffers && index >= 0, NULL);
+    if (codec->input_buffers[index].object)
+      return gst_amc_buffer_copy (&codec->input_buffers[index]);
+    else
+      return NULL;
+  }
+
+  if (!gst_amc_jni_call_object_method (env, err, codec->object,
+          media_codec.get_input_buffer, &buffer, index))
+    goto done;
+
+  if (buffer != NULL) {
+    ret = g_new0 (RealBuffer, 1);
+    ret->object = gst_amc_jni_object_make_global (env, buffer);
+    if (!ret->object) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED, "Failed to create global buffer reference");
+      goto error;
+    }
+
+    ret->data = (*env)->GetDirectBufferAddress (env, ret->object);
+    if (!ret->data) {
+      gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+          GST_LIBRARY_ERROR_FAILED, "Failed to get buffer address");
+      goto error;
+    }
+    ret->size = (*env)->GetDirectBufferCapacity (env, ret->object);
+  }
+
+done:
+
+  return (GstAmcBuffer *) ret;
+
+error:
+  if (ret->object)
+    gst_amc_jni_object_unref (env, ret->object);
+  g_free (ret);
+
+  return NULL;
+}
+
+gint
+gst_amc_codec_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs,
+    GError ** err)
+{
+  JNIEnv *env;
+  gint ret = G_MININT;
+
+  g_return_val_if_fail (codec != NULL, G_MININT);
+
+  env = gst_amc_jni_get_env ();
+  if (!gst_amc_jni_call_int_method (env, err, codec->object,
+          media_codec.dequeue_input_buffer, &ret, timeoutUs))
+    return G_MININT;
+
+  return ret;
+}
+
+static gboolean
+gst_amc_codec_fill_buffer_info (JNIEnv * env, jobject buffer_info,
+    GstAmcBufferInfo * info, GError ** err)
+{
+  g_return_val_if_fail (buffer_info != NULL, FALSE);
+
+  if (!gst_amc_jni_get_int_field (env, err, buffer_info,
+          media_codec_buffer_info.flags, &info->flags))
+    return FALSE;
+
+  if (!gst_amc_jni_get_int_field (env, err, buffer_info,
+          media_codec_buffer_info.offset, &info->offset))
+    return FALSE;
+
+  if (!gst_amc_jni_get_long_field (env, err, buffer_info,
+          media_codec_buffer_info.presentation_time_us,
+          &info->presentation_time_us))
+    return FALSE;
+
+  if (!gst_amc_jni_get_int_field (env, err, buffer_info,
+          media_codec_buffer_info.size, &info->size))
+    return FALSE;
+
+  return TRUE;
+}
+
+gint
+gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec,
+    GstAmcBufferInfo * info, gint64 timeoutUs, GError ** err)
+{
+  JNIEnv *env;
+  gint ret = G_MININT;
+  jobject info_o = NULL;
+
+  g_return_val_if_fail (codec != NULL, G_MININT);
+
+  env = gst_amc_jni_get_env ();
+
+  info_o =
+      gst_amc_jni_new_object (env, err, FALSE, media_codec_buffer_info.klass,
+      media_codec_buffer_info.constructor);
+  if (!info_o)
+    goto done;
+
+  if (!gst_amc_jni_call_int_method (env, err, codec->object,
+          media_codec.dequeue_output_buffer, &ret, info_o, timeoutUs)) {
+    ret = G_MININT;
+    goto done;
+  }
+
+  if (ret == INFO_OUTPUT_BUFFERS_CHANGED || ret == INFO_OUTPUT_FORMAT_CHANGED
+      || (ret >= 0 && !codec->output_buffers
+          && !media_codec.get_output_buffer)) {
+    if (!media_codec.get_output_buffer) {
+      if (codec->output_buffers)
+        gst_amc_jni_free_buffer_array (env, codec->output_buffers,
+            codec->n_output_buffers);
+      codec->output_buffers =
+          gst_amc_codec_get_output_buffers (codec,
+          &codec->n_output_buffers, err);
+      if (!codec->output_buffers) {
+        ret = G_MININT;
+        goto done;
+      }
+    }
+    if (ret == INFO_OUTPUT_BUFFERS_CHANGED) {
+      gst_amc_jni_object_local_unref (env, info_o);
+      return gst_amc_codec_dequeue_output_buffer (codec, info, timeoutUs, err);
+    }
+  } else if (ret < 0) {
+    goto done;
+  }
+
+  if (ret >= 0 && !gst_amc_codec_fill_buffer_info (env, info_o, info, err)) {
+    ret = G_MININT;
+    goto done;
+  }
+
+done:
+  if (info_o)
+    gst_amc_jni_object_local_unref (env, info_o);
+  info_o = NULL;
+
+  return ret;
+}
+
+gboolean
+gst_amc_codec_queue_input_buffer (GstAmcCodec * codec, gint index,
+    const GstAmcBufferInfo * info, GError ** err)
+{
+  JNIEnv *env;
+
+  g_return_val_if_fail (codec != NULL, FALSE);
+  g_return_val_if_fail (info != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+  return gst_amc_jni_call_void_method (env, err, codec->object,
+      media_codec.queue_input_buffer, index, info->offset, info->size,
+      info->presentation_time_us, info->flags);
+}
+
+gboolean
+gst_amc_codec_release_output_buffer (GstAmcCodec * codec, gint index,
+    gboolean render, GError ** err)
+{
+  JNIEnv *env;
+
+  g_return_val_if_fail (codec != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+  return gst_amc_jni_call_void_method (env, err, codec->object,
+      media_codec.release_output_buffer, index, render);
+}
diff --git a/sys/androidmedia/jni/gstamc-format-jni.c b/sys/androidmedia/jni/gstamc-format-jni.c
new file mode 100644 (file)
index 0000000..8d37056
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * 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-format.h"
+#include "gstamc-internal-jni.h"
+
+static struct
+{
+  jclass klass;
+  jmethodID create_audio_format;
+  jmethodID create_video_format;
+  jmethodID to_string;
+  jmethodID contains_key;
+  jmethodID get_float;
+  jmethodID set_float;
+  jmethodID get_integer;
+  jmethodID set_integer;
+  jmethodID get_string;
+  jmethodID set_string;
+  jmethodID get_byte_buffer;
+  jmethodID set_byte_buffer;
+} media_format;
+
+gboolean
+gst_amc_format_static_init (void)
+{
+  gboolean ret = TRUE;
+  JNIEnv *env;
+  jclass tmp;
+
+  env = gst_amc_jni_get_env ();
+
+  tmp = (*env)->FindClass (env, "android/media/MediaFormat");
+  if (!tmp) {
+    ret = FALSE;
+    GST_ERROR ("Failed to get format class");
+    if ((*env)->ExceptionCheck (env)) {
+      (*env)->ExceptionDescribe (env);
+      (*env)->ExceptionClear (env);
+    }
+    goto done;
+  }
+  media_format.klass = (*env)->NewGlobalRef (env, tmp);
+  if (!media_format.klass) {
+    ret = FALSE;
+    GST_ERROR ("Failed to get format class global reference");
+    if ((*env)->ExceptionCheck (env)) {
+      (*env)->ExceptionDescribe (env);
+      (*env)->ExceptionClear (env);
+    }
+    goto done;
+  }
+  (*env)->DeleteLocalRef (env, tmp);
+  tmp = NULL;
+
+  media_format.create_audio_format =
+      (*env)->GetStaticMethodID (env, media_format.klass, "createAudioFormat",
+      "(Ljava/lang/String;II)Landroid/media/MediaFormat;");
+  media_format.create_video_format =
+      (*env)->GetStaticMethodID (env, media_format.klass, "createVideoFormat",
+      "(Ljava/lang/String;II)Landroid/media/MediaFormat;");
+  media_format.to_string =
+      (*env)->GetMethodID (env, media_format.klass, "toString",
+      "()Ljava/lang/String;");
+  media_format.contains_key =
+      (*env)->GetMethodID (env, media_format.klass, "containsKey",
+      "(Ljava/lang/String;)Z");
+  media_format.get_float =
+      (*env)->GetMethodID (env, media_format.klass, "getFloat",
+      "(Ljava/lang/String;)F");
+  media_format.set_float =
+      (*env)->GetMethodID (env, media_format.klass, "setFloat",
+      "(Ljava/lang/String;F)V");
+  media_format.get_integer =
+      (*env)->GetMethodID (env, media_format.klass, "getInteger",
+      "(Ljava/lang/String;)I");
+  media_format.set_integer =
+      (*env)->GetMethodID (env, media_format.klass, "setInteger",
+      "(Ljava/lang/String;I)V");
+  media_format.get_string =
+      (*env)->GetMethodID (env, media_format.klass, "getString",
+      "(Ljava/lang/String;)Ljava/lang/String;");
+  media_format.set_string =
+      (*env)->GetMethodID (env, media_format.klass, "setString",
+      "(Ljava/lang/String;Ljava/lang/String;)V");
+  media_format.get_byte_buffer =
+      (*env)->GetMethodID (env, media_format.klass, "getByteBuffer",
+      "(Ljava/lang/String;)Ljava/nio/ByteBuffer;");
+  media_format.set_byte_buffer =
+      (*env)->GetMethodID (env, media_format.klass, "setByteBuffer",
+      "(Ljava/lang/String;Ljava/nio/ByteBuffer;)V");
+  if (!media_format.create_audio_format || !media_format.create_video_format
+      || !media_format.contains_key || !media_format.get_float
+      || !media_format.set_float || !media_format.get_integer
+      || !media_format.set_integer || !media_format.get_string
+      || !media_format.set_string || !media_format.get_byte_buffer
+      || !media_format.set_byte_buffer) {
+    ret = FALSE;
+    GST_ERROR ("Failed to get format methods");
+    if ((*env)->ExceptionCheck (env)) {
+      (*env)->ExceptionDescribe (env);
+      (*env)->ExceptionClear (env);
+    }
+    goto done;
+  }
+
+done:
+  if (tmp)
+    (*env)->DeleteLocalRef (env, tmp);
+  tmp = NULL;
+
+  return ret;
+}
+
+GstAmcFormat *
+gst_amc_format_new_audio (const gchar * mime, gint sample_rate, gint channels,
+    GError ** err)
+{
+  JNIEnv *env;
+  GstAmcFormat *format = NULL;
+  jstring mime_str;
+
+  g_return_val_if_fail (mime != NULL, NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  mime_str = gst_amc_jni_string_from_gchar (env, err, FALSE, mime);
+  if (!mime_str)
+    goto error;
+
+  format = g_slice_new0 (GstAmcFormat);
+  format->object =
+      gst_amc_jni_new_object_from_static (env, err, TRUE, media_format.klass,
+      media_format.create_audio_format, mime_str, sample_rate, channels);
+  if (!format->object)
+    goto error;
+
+done:
+  if (mime_str)
+    gst_amc_jni_object_local_unref (env, mime_str);
+  mime_str = NULL;
+
+  return format;
+
+error:
+  if (format)
+    g_slice_free (GstAmcFormat, format);
+  format = NULL;
+  goto done;
+}
+
+GstAmcFormat *
+gst_amc_format_new_video (const gchar * mime, gint width, gint height,
+    GError ** err)
+{
+  JNIEnv *env;
+  GstAmcFormat *format = NULL;
+  jstring mime_str;
+
+  g_return_val_if_fail (mime != NULL, NULL);
+
+  env = gst_amc_jni_get_env ();
+
+  mime_str = gst_amc_jni_string_from_gchar (env, err, FALSE, mime);
+  if (!mime_str)
+    goto error;
+
+  format = g_slice_new0 (GstAmcFormat);
+  format->object =
+      gst_amc_jni_new_object_from_static (env, err, TRUE, media_format.klass,
+      media_format.create_video_format, mime_str, width, height);
+  if (!format->object)
+    goto error;
+
+done:
+  if (mime_str)
+    gst_amc_jni_object_local_unref (env, mime_str);
+  mime_str = NULL;
+
+  return format;
+
+error:
+  if (format)
+    g_slice_free (GstAmcFormat, format);
+  format = NULL;
+  goto done;
+}
+
+void
+gst_amc_format_free (GstAmcFormat * format)
+{
+  JNIEnv *env;
+
+  g_return_if_fail (format != NULL);
+
+  env = gst_amc_jni_get_env ();
+  gst_amc_jni_object_unref (env, format->object);
+  g_slice_free (GstAmcFormat, format);
+}
+
+gchar *
+gst_amc_format_to_string (GstAmcFormat * format, GError ** err)
+{
+  JNIEnv *env;
+  jstring v_str = NULL;
+  gchar *ret = NULL;
+
+  g_return_val_if_fail (format != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  if (!gst_amc_jni_call_object_method (env, err, format->object,
+          media_format.to_string, &v_str))
+    goto done;
+  ret = gst_amc_jni_string_to_gchar (env, v_str, TRUE);
+
+done:
+
+  return ret;
+}
+
+gboolean
+gst_amc_format_contains_key (GstAmcFormat * format, const gchar * key,
+    GError ** err)
+{
+  JNIEnv *env;
+  gboolean ret = FALSE;
+  jstring key_str = NULL;
+
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (key != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
+  if (!key_str)
+    goto done;
+
+  if (!gst_amc_jni_call_boolean_method (env, err, format->object,
+          media_format.contains_key, &ret, key_str))
+    goto done;
+
+done:
+  if (key_str)
+    gst_amc_jni_object_local_unref (env, key_str);
+
+  return ret;
+}
+
+gboolean
+gst_amc_format_get_float (GstAmcFormat * format, const gchar * key,
+    gfloat * value, GError ** err)
+{
+  JNIEnv *env;
+  gboolean ret = FALSE;
+  jstring key_str = NULL;
+
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (key != NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+
+  *value = 0;
+  env = gst_amc_jni_get_env ();
+
+  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
+  if (!key_str)
+    goto done;
+
+  if (!gst_amc_jni_call_float_method (env, err, format->object,
+          media_format.get_float, value, key_str))
+    goto done;
+  ret = TRUE;
+
+done:
+  if (key_str)
+    gst_amc_jni_object_local_unref (env, key_str);
+
+  return ret;
+}
+
+gboolean
+gst_amc_format_set_float (GstAmcFormat * format, const gchar * key,
+    gfloat value, GError ** err)
+{
+  JNIEnv *env;
+  jstring key_str = NULL;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (key != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
+  if (!key_str)
+    goto done;
+
+  if (!gst_amc_jni_call_void_method (env, err, format->object,
+          media_format.set_float, key_str, value))
+    goto done;
+
+  ret = TRUE;
+
+done:
+  if (key_str)
+    gst_amc_jni_object_local_unref (env, key_str);
+
+  return ret;
+}
+
+gboolean
+gst_amc_format_get_int (GstAmcFormat * format, const gchar * key, gint * value,
+    GError ** err)
+{
+  JNIEnv *env;
+  gboolean ret = FALSE;
+  jstring key_str = NULL;
+
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (key != NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+
+  *value = 0;
+  env = gst_amc_jni_get_env ();
+
+  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
+  if (!key_str)
+    goto done;
+
+  if (!gst_amc_jni_call_int_method (env, err, format->object,
+          media_format.get_integer, value, key_str))
+    goto done;
+  ret = TRUE;
+
+done:
+  if (key_str)
+    gst_amc_jni_object_local_unref (env, key_str);
+
+  return ret;
+
+}
+
+gboolean
+gst_amc_format_set_int (GstAmcFormat * format, const gchar * key, gint value,
+    GError ** err)
+{
+  JNIEnv *env;
+  jstring key_str = NULL;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (key != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
+  if (!key_str)
+    goto done;
+
+  if (!gst_amc_jni_call_void_method (env, err, format->object,
+          media_format.set_integer, key_str, value))
+    goto done;
+
+  ret = TRUE;
+
+done:
+  if (key_str)
+    gst_amc_jni_object_local_unref (env, key_str);
+
+  return ret;
+}
+
+gboolean
+gst_amc_format_get_string (GstAmcFormat * format, const gchar * key,
+    gchar ** value, GError ** err)
+{
+  JNIEnv *env;
+  gboolean ret = FALSE;
+  jstring key_str = NULL;
+  jstring v_str = NULL;
+
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (key != NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+
+  *value = 0;
+  env = gst_amc_jni_get_env ();
+
+  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
+  if (!key_str)
+    goto done;
+
+  if (!gst_amc_jni_call_object_method (env, err, format->object,
+          media_format.get_string, &v_str, key_str))
+    goto done;
+
+  *value = gst_amc_jni_string_to_gchar (env, v_str, TRUE);
+
+  ret = TRUE;
+
+done:
+  if (key_str)
+    gst_amc_jni_object_local_unref (env, key_str);
+
+  return ret;
+}
+
+gboolean
+gst_amc_format_set_string (GstAmcFormat * format, const gchar * key,
+    const gchar * value, GError ** err)
+{
+  JNIEnv *env;
+  jstring key_str = NULL;
+  jstring v_str = NULL;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (key != NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
+  if (!key_str)
+    goto done;
+
+  v_str = gst_amc_jni_string_from_gchar (env, err, FALSE, value);
+  if (!v_str)
+    goto done;
+
+  if (!gst_amc_jni_call_void_method (env, err, format->object,
+          media_format.set_string, key_str, v_str))
+    goto done;
+
+  ret = TRUE;
+
+done:
+  if (key_str)
+    gst_amc_jni_object_local_unref (env, key_str);
+  if (v_str)
+    gst_amc_jni_object_local_unref (env, v_str);
+
+  return ret;
+}
+
+gboolean
+gst_amc_format_get_buffer (GstAmcFormat * format, const gchar * key,
+    guint8 ** data, gsize * size, GError ** err)
+{
+  JNIEnv *env;
+  gboolean ret = FALSE;
+  jstring key_str = NULL;
+  jobject v = NULL;
+  RealBuffer buf = { 0, };
+  gint position = 0, limit = 0;
+
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (key != NULL, FALSE);
+  g_return_val_if_fail (data != NULL, FALSE);
+  g_return_val_if_fail (size != NULL, FALSE);
+
+  *data = NULL;
+  *size = 0;
+  env = gst_amc_jni_get_env ();
+
+  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
+  if (!key_str)
+    goto done;
+
+  if (!gst_amc_jni_call_object_method (env, err, format->object,
+          media_format.get_byte_buffer, &v, key_str))
+    goto done;
+
+  *data = (*env)->GetDirectBufferAddress (env, v);
+  if (*data == NULL) {
+    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+        GST_LIBRARY_ERROR_FAILED, "Failed get buffer address");
+    goto done;
+  }
+  *size = (*env)->GetDirectBufferCapacity (env, v);
+
+  buf.object = v;
+  buf.data = *data;
+  buf.size = *size;
+  gst_amc_buffer_get_position_and_limit (&buf, NULL, &position, &limit);
+  *size = limit;
+
+  *data = g_memdup (*data + position, limit);
+
+  ret = TRUE;
+
+done:
+  if (key_str)
+    gst_amc_jni_object_local_unref (env, key_str);
+  if (v)
+    gst_amc_jni_object_local_unref (env, v);
+
+  return ret;
+}
+
+gboolean
+gst_amc_format_set_buffer (GstAmcFormat * format, const gchar * key,
+    guint8 * data, gsize size, GError ** err)
+{
+  JNIEnv *env;
+  jstring key_str = NULL;
+  jobject v = NULL;
+  gboolean ret = FALSE;
+  RealBuffer buf = { 0, };
+
+  g_return_val_if_fail (format != NULL, FALSE);
+  g_return_val_if_fail (key != NULL, FALSE);
+  g_return_val_if_fail (data != NULL, FALSE);
+
+  env = gst_amc_jni_get_env ();
+
+  key_str = gst_amc_jni_string_from_gchar (env, err, FALSE, key);
+  if (!key_str)
+    goto done;
+
+  /* FIXME: The memory must remain valid until the codec is stopped */
+  v = (*env)->NewDirectByteBuffer (env, data, size);
+  if (!v) {
+    gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
+        GST_LIBRARY_ERROR_FAILED, "Failed create Java byte buffer");
+    goto done;
+  }
+
+  buf.object = v;
+  buf.data = data;
+  buf.size = size;
+
+  gst_amc_buffer_set_position_and_limit ((GstAmcBuffer *) & buf, NULL, 0, size);
+
+  if (!gst_amc_jni_call_void_method (env, err, format->object,
+          media_format.set_byte_buffer, key_str, v))
+    goto done;
+
+  ret = TRUE;
+
+done:
+  if (key_str)
+    gst_amc_jni_object_local_unref (env, key_str);
+  if (v)
+    gst_amc_jni_object_local_unref (env, v);
+
+  return ret;
+}
diff --git a/sys/androidmedia/jni/gstamc-internal-jni.h b/sys/androidmedia/jni/gstamc-internal-jni.h
new file mode 100644 (file)
index 0000000..a1c6558
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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_INTERNAL_JNI_H__
+#define __GST_AMC_INTERNAL_JNI_H__
+
+#include "../gstamc-codec.h"
+#include "../gstamc-format.h"
+
+G_BEGIN_DECLS
+
+struct _GstAmcFormat
+{
+  /* < private > */
+  jobject object;               /* global reference */
+};
+
+typedef struct
+{
+  guint8 *data;
+  gsize size;
+
+  jobject object;               /* global reference */
+} RealBuffer;
+
+gboolean
+gst_amc_buffer_get_position_and_limit (RealBuffer * buffer_, GError ** err,
+    gint * position, gint * limit);
+
+G_END_DECLS
+
+#endif /* __GST_AMC_INTERNAL_JNI_H__ */
index d41d399..79bc2f8 100644 (file)
@@ -10,7 +10,9 @@ androidmedia_sources = [
   'gst-android-graphics-imageformat.c',
   'gst-android-hardware-camera.c',
   'gst-android-hardware-sensor.c',
-  'gstjniutils.c'
+  'gstjniutils.c',
+  'jni/gstamc-codec-jni.c',
+  'jni/gstamc-format-jni.c',
 ]
 
 androidmedia_java_sources = [