/* * Copyright (C) 2012,2018 Collabora Ltd. * Author: Sebastian Dröge * Copyright (C) 2015, Sebastian Dröge * * 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" #include "gstamcsurfacetexture-jni.h" #include "gstamcsurface.h" 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; } 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, "", "()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, 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; } 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; g_clear_object (&codec->surface); gst_amc_jni_object_unref (env, codec->object); g_slice_free (GstAmcCodec, codec); } gboolean gst_amc_codec_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 (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, 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); } GstAmcSurfaceTexture * gst_amc_codec_new_surface_texture (GError ** err) { return (GstAmcSurfaceTexture *) gst_amc_surface_texture_jni_new (err); }