mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 11:41:09 +00:00
a50ce9c6b0
When implementing NDK media support, it would be useful to also have JNI implementation in the same binary as NDK media compatibility is lower. As such, implement a rudimentary vtable system for gstamc-codec and gstamc-format, and allow choosing the implementation at static_init() time. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4115>
556 lines
14 KiB
C
556 lines
14 KiB
C
/*
|
|
* 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-jni.h"
|
|
#include "gstamc-internal-jni.h"
|
|
|
|
static struct
|
|
{
|
|
jclass klass;
|
|
jmethodID create_audio_format;
|
|
jmethodID create_video_format;
|
|
jmethodID to_string;
|
|
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_jni_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.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.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 GstAmcFormat *
|
|
gst_amc_format_jni_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;
|
|
}
|
|
|
|
static GstAmcFormat *
|
|
gst_amc_format_jni_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;
|
|
}
|
|
|
|
static void
|
|
gst_amc_format_jni_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);
|
|
}
|
|
|
|
static gchar *
|
|
gst_amc_format_jni_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;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amc_format_jni_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;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amc_format_jni_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;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amc_format_jni_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;
|
|
|
|
}
|
|
|
|
static gboolean
|
|
gst_amc_format_jni_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;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amc_format_jni_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;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amc_format_jni_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;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amc_format_jni_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_memdup2 (*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;
|
|
}
|
|
|
|
static gboolean
|
|
gst_amc_format_jni_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;
|
|
}
|
|
|
|
GstAmcFormatVTable gst_amc_format_jni_vtable = {
|
|
.new_audio = gst_amc_format_jni_new_audio,
|
|
.new_video = gst_amc_format_jni_new_video,
|
|
.free = gst_amc_format_jni_free,
|
|
|
|
.to_string = gst_amc_format_jni_to_string,
|
|
|
|
.get_float = gst_amc_format_jni_get_float,
|
|
.set_float = gst_amc_format_jni_set_float,
|
|
.get_int = gst_amc_format_jni_get_int,
|
|
.set_int = gst_amc_format_jni_set_int,
|
|
.get_string = gst_amc_format_jni_get_string,
|
|
.set_string = gst_amc_format_jni_set_string,
|
|
.get_buffer = gst_amc_format_jni_get_buffer,
|
|
.set_buffer = gst_amc_format_jni_set_buffer,
|
|
};
|