gstreamer/subprojects/gst-plugins-bad/sys/androidmedia/jni/gstamc-codec-jni.c
Ratchanan Srirattanamet a50ce9c6b0 androidmedia: allow multiple implementations of codec and format
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>
2024-01-11 19:00:13 +00:00

1126 lines
30 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-codec.h"
#include "../gstamc-constants.h"
#include "gstamc-jni.h"
#include "gstamc-internal-jni.h"
#include "gstamcsurfacetexture-jni.h"
#include "gstamcsurface.h"
#define PARAMETER_KEY_REQUEST_SYNC_FRAME "request-sync"
#define PARAMETER_KEY_VIDEO_BITRATE "video-bitrate"
struct _GstAmcCodec
{
jobject object; /* global reference */
RealBuffer *input_buffers, *output_buffers;
gsize n_input_buffers, n_output_buffers;
GstAmcSurface *surface;
gboolean is_encoder;
};
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;
jmethodID setParameters;
} 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 constructor;
jmethodID putInt;
} bundle_class;
static struct
{
jclass klass;
jmethodID get_limit, get_position;
jmethodID set_limit, set_position;
jmethodID clear;
} java_nio_buffer;
gboolean
gst_amc_codec_jni_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;
}
media_codec.setParameters =
(*env)->GetMethodID (env, media_codec.klass, "setParameters",
"(Landroid/os/Bundle;)V");
if ((*env)->ExceptionCheck (env))
(*env)->ExceptionClear (env);
/* 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);
if (media_codec.setParameters != NULL) {
/* Bundle needed for parameter setting on Android >= 19 */
tmp = (*env)->FindClass (env, "android/os/Bundle");
if (!tmp) {
ret = FALSE;
GST_ERROR ("Failed to get Bundle class");
if ((*env)->ExceptionCheck (env)) {
(*env)->ExceptionDescribe (env);
(*env)->ExceptionClear (env);
}
goto done;
}
bundle_class.klass = (*env)->NewGlobalRef (env, tmp);
if (!bundle_class.klass) {
ret = FALSE;
GST_ERROR ("Failed to get Bundle class global reference");
if ((*env)->ExceptionCheck (env)) {
(*env)->ExceptionDescribe (env);
(*env)->ExceptionClear (env);
}
goto done;
}
(*env)->DeleteLocalRef (env, tmp);
tmp = NULL;
bundle_class.constructor =
(*env)->GetMethodID (env, bundle_class.klass, "<init>", "()V");
bundle_class.putInt =
(*env)->GetMethodID (env, bundle_class.klass, "putInt",
"(Ljava/lang/String;I)V");
if (!bundle_class.constructor || !bundle_class.putInt) {
ret = FALSE;
GST_ERROR ("Failed to get Bundle 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 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;
}
static void
gst_amc_buffer_jni_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;
}
static gboolean
gst_amc_buffer_jni_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;
}
static GstAmcCodec *
gst_amc_codec_jni_new (const gchar * name, gboolean is_encoder, 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);
codec->is_encoder = is_encoder;
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;
}
static void
gst_amc_codec_jni_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;
g_clear_object (&codec->surface);
gst_amc_jni_object_unref (env, codec->object);
g_slice_free (GstAmcCodec, codec);
}
static gboolean
gst_amc_codec_jni_configure (GstAmcCodec * codec, GstAmcFormat * format,
GstAmcSurfaceTexture * surface, GError ** err)
{
JNIEnv *env;
gint flags = 0;
g_return_val_if_fail (codec != NULL, FALSE);
g_return_val_if_fail (format != NULL, FALSE);
g_return_val_if_fail (surface == NULL
|| GST_IS_AMC_SURFACE_TEXTURE_JNI (surface), FALSE);
env = gst_amc_jni_get_env ();
if (surface) {
g_object_unref (codec->surface);
codec->surface =
gst_amc_surface_new ((GstAmcSurfaceTextureJNI *) surface, err);
if (!codec->surface)
return FALSE;
}
if (codec->is_encoder)
flags = 1;
return gst_amc_jni_call_void_method (env, err, codec->object,
media_codec.configure, format->object,
codec->surface ? codec->surface->jobject : NULL, NULL, flags);
}
static GstAmcFormat *
gst_amc_codec_jni_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_jni_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_jni_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;
}
static gboolean
gst_amc_codec_jni_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_jni_get_input_buffers (codec, &codec->n_input_buffers,
err);
if (!codec->input_buffers) {
gst_amc_codec_stop (codec, NULL);
return FALSE;
}
}
return ret;
}
static gboolean
gst_amc_codec_jni_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);
}
static gboolean
gst_amc_codec_jni_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);
}
static gboolean
gst_amc_codec_jni_set_parameter (GstAmcCodec * codec, JNIEnv * env,
GError ** err, const gchar * key, int value)
{
gboolean ret = FALSE;
jobject bundle = NULL;
jstring jkey = NULL;
if (media_codec.setParameters == NULL)
goto done; // Not available means we're on Android < 19
bundle = gst_amc_jni_new_object (env, err, FALSE, bundle_class.klass,
bundle_class.constructor);
if (!bundle)
goto done;
jkey = (*env)->NewStringUTF (env, key);
if (!gst_amc_jni_call_void_method (env, err,
bundle, bundle_class.putInt, jkey, value))
goto done;
if (!gst_amc_jni_call_void_method (env, err, codec->object,
media_codec.setParameters, bundle))
goto done;
ret = TRUE;
done:
if (jkey)
(*env)->DeleteLocalRef (env, jkey);
if (bundle)
(*env)->DeleteLocalRef (env, bundle);
return ret;
}
static gboolean
gst_amc_codec_jni_request_key_frame (GstAmcCodec * codec, GError ** err)
{
JNIEnv *env;
g_return_val_if_fail (codec != NULL, FALSE);
env = gst_amc_jni_get_env ();
return gst_amc_codec_jni_set_parameter (codec, env, err,
PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
}
static gboolean
gst_amc_codec_jni_have_dynamic_bitrate ()
{
/* Dynamic bitrate scaling is supported on Android >= 19,
* where the setParameters() call is available */
return (media_codec.setParameters != NULL);
}
static gboolean
gst_amc_codec_jni_set_dynamic_bitrate (GstAmcCodec * codec, GError ** err,
gint bitrate)
{
JNIEnv *env;
g_return_val_if_fail (codec != NULL, FALSE);
env = gst_amc_jni_get_env ();
return gst_amc_codec_jni_set_parameter (codec, env, err,
PARAMETER_KEY_VIDEO_BITRATE, bitrate);
}
static gboolean
gst_amc_codec_jni_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_jni_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;
}
static GstAmcBuffer *
gst_amc_codec_jni_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;
}
static gint
gst_amc_codec_jni_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_jni_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;
}
static gint
gst_amc_codec_jni_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_jni_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_jni_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;
}
static gboolean
gst_amc_codec_jni_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);
}
static gboolean
gst_amc_codec_jni_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);
}
static GstAmcSurfaceTexture *
gst_amc_codec_jni_new_surface_texture (GError ** err)
{
return (GstAmcSurfaceTexture *) gst_amc_surface_texture_jni_new (err);
}
GstAmcCodecVTable gst_amc_codec_jni_vtable = {
.buffer_free = gst_amc_buffer_jni_free,
.buffer_set_position_and_limit = gst_amc_buffer_jni_set_position_and_limit,
.create = gst_amc_codec_jni_new,
.free = gst_amc_codec_jni_free,
.configure = gst_amc_codec_jni_configure,
.get_output_format = gst_amc_codec_jni_get_output_format,
.start = gst_amc_codec_jni_start,
.stop = gst_amc_codec_jni_stop,
.flush = gst_amc_codec_jni_flush,
.request_key_frame = gst_amc_codec_jni_request_key_frame,
.have_dynamic_bitrate = gst_amc_codec_jni_have_dynamic_bitrate,
.set_dynamic_bitrate = gst_amc_codec_jni_set_dynamic_bitrate,
.release = gst_amc_codec_jni_release,
.get_output_buffer = gst_amc_codec_jni_get_output_buffer,
.get_input_buffer = gst_amc_codec_jni_get_input_buffer,
.dequeue_input_buffer = gst_amc_codec_jni_dequeue_input_buffer,
.dequeue_output_buffer = gst_amc_codec_jni_dequeue_output_buffer,
.queue_input_buffer = gst_amc_codec_jni_queue_input_buffer,
.release_output_buffer = gst_amc_codec_jni_release_output_buffer,
.new_surface_texture = gst_amc_codec_jni_new_surface_texture,
};