gstreamer/sys/androidmedia/gst-android-media-mediacodec.c
Youness Alaoui 3ac90867ac androidmedia: Use gst-dvm and refactor java wrappers (WIP)
Moved the java wrapper API into its own files and made use of the
gst-dvm macros. Also renamed the API to have the proper naming
convention and coding style in order to match the one in androidcamera.
This is a work in progress! "android/media/MediaCodecList" is still missing
and the actual elements have not been ported to use the new function names.
2016-01-21 13:49:14 -05:00

534 lines
14 KiB
C

/*
* Copyright (C) 2012, Collabora Ltd.
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
* Author: Youness Alaoui <youness.alaoui@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
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/dvm/gst-dvm.h>
#include "gst-android-media-mediacodec.h"
static struct
{
jclass klass;
jmethodID constructor;
jfieldID flags;
jfieldID offset;
jfieldID presentationTimeUs;
jfieldID size;
} android_media_mediacodec_bufferinfo;
static struct
{
jclass klass;
jmethodID configure;
jmethodID createByCodecName;
jmethodID createDecoderByType;
jmethodID createEncoderByType;
jmethodID dequeueInputBuffer;
jmethodID dequeueOutputBuffer;
jmethodID flush;
jmethodID getInputBuffers;
jmethodID getOutputBuffers;
jmethodID getOutputFormat;
jmethodID queueInputBuffer;
jmethodID release;
jmethodID releaseOutputBuffer;
jmethodID start;
jmethodID stop;
} android_media_mediacodec = {
0};
static gboolean
_init_classes (void)
{
JNIEnv *env = gst_dvm_get_env ();
/* android.media.MediaCodec */
GST_DVM_GET_CLASS (android_media_mediacodec, "android/media/MediaCodec");
GST_DVM_GET_STATIC_METHOD (android_media_mediacodec, createByCodecName,
"(Ljava/lang/String;)Landroid/media/MediaCodec;");
GST_DVM_GET_STATIC_METHOD (android_media_mediacodec, createDecoderByType,
"(Ljava/lang/String;)Landroid/media/MediaCodec;");
GST_DVM_GET_STATIC_METHOD (android_media_mediacodec, createEncoderByType,
"(Ljava/lang/String;)Landroid/media/MediaCodec;");
GST_DVM_GET_METHOD (android_media_mediacodec, configure,
"(Landroid/media/MediaFormat;Landroid/view/Surface;"
"Landroid/media/MediaCrypto;I)V");
GST_DVM_GET_METHOD (android_media_mediacodec, dequeueInputBuffer, "(J)I");
GST_DVM_GET_METHOD (android_media_mediacodec, dequeueOutputBuffer,
"(Landroid/media/MediaCodec$BufferInfo;J)I");
GST_DVM_GET_METHOD (android_media_mediacodec, flush, "()V");
GST_DVM_GET_METHOD (android_media_mediacodec, getInputBuffers,
"()[Ljava/nio/ByteBuffer;");
GST_DVM_GET_METHOD (android_media_mediacodec, getOutputBuffers,
"()[Ljava/nio/ByteBuffer;");
GST_DVM_GET_METHOD (android_media_mediacodec, getOutputFormat,
"()Landroid/media/MediaFormat;");
GST_DVM_GET_METHOD (android_media_mediacodec, queueInputBuffer, "(IIIJI)V");
GST_DVM_GET_METHOD (android_media_mediacodec, release, "()V");
GST_DVM_GET_METHOD (android_media_mediacodec, releaseOutputBuffer, "(IZ)V");
GST_DVM_GET_METHOD (android_media_mediacodec, start, "()V");
GST_DVM_GET_METHOD (android_media_mediacodec, stop, "()V");
/* android.media.MediaCodec.BufferInfo */
GST_DVM_GET_CLASS (android_media_mediacodec_bufferinfo,
"android/media/MediaCodec$BufferInfo");
GST_DVM_GET_CONSTRUCTOR (android_media_mediacodec_bufferinfo, constructor,
"()V");
GST_DVM_GET_FIELD (android_media_mediacodec_bufferinfo, flags, "I");
GST_DVM_GET_FIELD (android_media_mediacodec_bufferinfo, offset, "I");
GST_DVM_GET_FIELD (android_media_mediacodec_bufferinfo, presentationTimeUs,
"J");
GST_DVM_GET_FIELD (android_media_mediacodec_bufferinfo, size, "I");
return TRUE;
}
gboolean
gst_android_media_mediacodec_init (void)
{
if (!_init_classes ()) {
gst_android_media_mediacodec_deinit ();
return FALSE;
}
return TRUE;
}
void
gst_android_media_mediacodec_deinit (void)
{
JNIEnv *env = gst_dvm_get_env ();
if (android_media_mediacodec.klass)
(*env)->DeleteGlobalRef (env, android_media_mediacodec.klass);
android_media_mediacodec.klass = NULL;
if (android_media_mediacodec_bufferinfo.klass)
(*env)->DeleteGlobalRef (env, android_media_mediacodec_bufferinfo.klass);
android_media_mediacodec_bufferinfo.klass = NULL;
}
/* android.media.MediaCodec */
#define AMMC_CALL(error_statement, type, method, ...) \
GST_DVM_CALL (error_statement, self->object, type, \
android_media_mediacodec, method, ## __VA_ARGS__);
#define AMMC_STATIC_CALL(error_statement, type, method, ...) \
GST_DVM_STATIC_CALL (error_statement, type, \
android_media_mediacodec, method, ## __VA_ARGS__);
gboolean
gst_am_mediacodec_configure (GstAmMediaCodec * self, GstAmMediaFormat * format,
gint flags)
{
JNIEnv *env = gst_dvm_get_env ();
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (format != NULL, FALSE);
AMMC_CALL (return FALSE, Void, configure, format->object, NULL, NULL, flags);
return TRUE;
}
GstAmMediaCodec *
gst_am_mediacodec_create_by_codec_name (const gchar * name)
{
JNIEnv *env = gst_dvm_get_env ();
GstAmMediaCodec *codec = NULL;
jobject object = NULL;
jstring name_str;
g_return_val_if_fail (name != NULL, NULL);
name_str = (*env)->NewStringUTF (env, name);
if (name_str == NULL)
goto done;
object = AMMC_STATIC_CALL (goto done, Object, createByCodecName, name_str);
if (object) {
codec = g_slice_new0 (GstAmMediaCodec);
codec->object = (*env)->NewGlobalRef (env, object);
(*env)->DeleteLocalRef (env, object);
if (!codec->object) {
GST_ERROR ("Failed to create global reference");
(*env)->ExceptionClear (env);
g_slice_free (GstAmMediaCodec, codec);
codec = NULL;
}
}
done:
if (name_str)
(*env)->DeleteLocalRef (env, name_str);
return codec;
}
GstAmMediaFormat *
gst_am_mediacodec_get_output_format (GstAmMediaCodec * self)
{
JNIEnv *env = gst_dvm_get_env ();
GstAmMediaFormat *format = NULL;
jobject object = NULL;
g_return_val_if_fail (self != NULL, NULL);
object = AMMC_CALL (return NULL, Object, getOutputFormat);
if (object) {
format = g_slice_new0 (GstAmMediaFormat);
format->object = (*env)->NewGlobalRef (env, object);
(*env)->DeleteLocalRef (env, object);
if (!format->object) {
GST_ERROR ("Failed to create global reference");
(*env)->ExceptionClear (env);
g_slice_free (GstAmMediaFormat, format);
return NULL;
}
}
return format;
}
gboolean
gst_am_mediacodec_start (GstAmMediaCodec * self)
{
JNIEnv *env = gst_dvm_get_env ();
g_return_val_if_fail (self != NULL, FALSE);
AMMC_CALL (return FALSE, Void, start);
return TRUE;
}
gboolean
gst_am_mediacodec_stop (GstAmMediaCodec * self)
{
JNIEnv *env = gst_dvm_get_env ();
g_return_val_if_fail (self != NULL, FALSE);
AMMC_CALL (return FALSE, Void, stop);
return TRUE;
}
gboolean
gst_am_mediacodec_flush (GstAmMediaCodec * self)
{
JNIEnv *env = gst_dvm_get_env ();
g_return_val_if_fail (self != NULL, FALSE);
AMMC_CALL (return FALSE, Void, flush);
return TRUE;
}
void
gst_am_mediacodec_release (GstAmMediaCodec * self)
{
JNIEnv *env = gst_dvm_get_env ();
g_return_if_fail (self != NULL);
AMMC_CALL (, Void, release);
(*env)->DeleteGlobalRef (env, self->object);
g_slice_free (GstAmMediaCodec, self);
}
void
gst_am_mediacodec_free_buffers (GstAmcBuffer * buffers, gsize n_buffers)
{
JNIEnv *env = gst_dvm_get_env ();
jsize i;
g_return_if_fail (buffers != NULL);
for (i = 0; i < n_buffers; i++) {
if (buffers[i].object)
(*env)->DeleteGlobalRef (env, buffers[i].object);
}
g_free (buffers);
}
GstAmcBuffer *
gst_am_mediacodec_get_output_buffers (GstAmMediaCodec * self, gsize * n_buffers)
{
JNIEnv *env = gst_dvm_get_env ();
jobject output_buffers = NULL;
jsize n_output_buffers;
GstAmcBuffer *ret = NULL;
jsize i;
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (n_buffers != NULL, NULL);
*n_buffers = 0;
output_buffers = AMMC_CALL (goto done, Object, getOutputBuffers);
if (!output_buffers)
goto done;
n_output_buffers = (*env)->GetArrayLength (env, output_buffers);
if ((*env)->ExceptionCheck (env)) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to get output buffers array length");
goto done;
}
*n_buffers = n_output_buffers;
ret = g_new0 (GstAmcBuffer, n_output_buffers);
for (i = 0; i < n_output_buffers; i++) {
jobject buffer = NULL;
buffer = (*env)->GetObjectArrayElement (env, output_buffers, i);
if ((*env)->ExceptionCheck (env) || !buffer) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to get output buffer %d", i);
goto error;
}
ret[i].object = (*env)->NewGlobalRef (env, buffer);
(*env)->DeleteLocalRef (env, buffer);
if (!ret[i].object) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to create global reference %d", i);
goto error;
}
ret[i].data = (*env)->GetDirectBufferAddress (env, ret[i].object);
if (!ret[i].data) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to get buffer address %d", i);
goto error;
}
ret[i].size = (*env)->GetDirectBufferCapacity (env, ret[i].object);
}
done:
if (output_buffers)
(*env)->DeleteLocalRef (env, output_buffers);
output_buffers = NULL;
return ret;
error:
if (ret)
gst_am_mediacodec_free_buffers (ret, n_output_buffers);
ret = NULL;
*n_buffers = 0;
goto done;
}
GstAmcBuffer *
gst_am_mediacodec_get_input_buffers (GstAmMediaCodec * self, gsize * n_buffers)
{
JNIEnv *env = gst_dvm_get_env ();
jobject input_buffers = NULL;
jsize n_input_buffers;
GstAmcBuffer *ret = NULL;
jsize i;
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (n_buffers != NULL, NULL);
*n_buffers = 0;
input_buffers = AMMC_CALL (goto done, Object, getOutputBuffers);
if (!input_buffers)
goto done;
n_input_buffers = (*env)->GetArrayLength (env, input_buffers);
if ((*env)->ExceptionCheck (env)) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to get input buffers array length");
goto done;
}
*n_buffers = n_input_buffers;
ret = g_new0 (GstAmcBuffer, n_input_buffers);
for (i = 0; i < n_input_buffers; i++) {
jobject buffer = NULL;
buffer = (*env)->GetObjectArrayElement (env, input_buffers, i);
if ((*env)->ExceptionCheck (env) || !buffer) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to get input buffer %d", i);
goto error;
}
ret[i].object = (*env)->NewGlobalRef (env, buffer);
(*env)->DeleteLocalRef (env, buffer);
if (!ret[i].object) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to create global reference %d", i);
goto error;
}
ret[i].data = (*env)->GetDirectBufferAddress (env, ret[i].object);
if (!ret[i].data) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to get buffer address %d", i);
goto error;
}
ret[i].size = (*env)->GetDirectBufferCapacity (env, ret[i].object);
}
done:
if (input_buffers)
(*env)->DeleteLocalRef (env, input_buffers);
input_buffers = NULL;
return ret;
error:
if (ret)
gst_am_mediacodec_free_buffers (ret, n_input_buffers);
ret = NULL;
*n_buffers = 0;
goto done;
}
gint
gst_am_mediacodec_dequeue_input_buffer (GstAmMediaCodec * self,
gint64 timeoutUs)
{
JNIEnv *env = gst_dvm_get_env ();
gint ret = G_MININT;
g_return_val_if_fail (self != NULL, G_MININT);
ret = AMMC_CALL (return G_MININT, Int, dequeueInputBuffer, timeoutUs);
return ret;
}
static gboolean
_fill_buffer_info (JNIEnv * env, jobject buffer_info, GstAmmcBufferInfo * info)
{
g_return_val_if_fail (buffer_info != NULL, FALSE);
info->flags = (*env)->GetIntField (env, buffer_info,
android_media_mediacodec_bufferinfo.flags);
if ((*env)->ExceptionCheck (env)) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to get buffer info field");
return FALSE;
}
info->offset = (*env)->GetIntField (env, buffer_info,
android_media_mediacodec_bufferinfo.offset);
if ((*env)->ExceptionCheck (env)) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to get buffer info field");
return FALSE;
}
info->presentation_time_us = (*env)->GetLongField (env, buffer_info,
android_media_mediacodec_bufferinfo.presentationTimeUs);
if ((*env)->ExceptionCheck (env)) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to get buffer info field");
return FALSE;
}
info->size = (*env)->GetIntField (env, buffer_info,
android_media_mediacodec_bufferinfo.size);
if ((*env)->ExceptionCheck (env)) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to get buffer info field");
return FALSE;
}
return TRUE;
}
gint
gst_am_mediacodec_dequeue_output_buffer (GstAmMediaCodec * self,
GstAmmcBufferInfo * info, gint64 timeoutUs)
{
JNIEnv *env = gst_dvm_get_env ();
gint ret = G_MININT;
jobject info_o = NULL;
g_return_val_if_fail (self != NULL, G_MININT);
info_o = (*env)->NewObject (env, android_media_mediacodec_bufferinfo.klass,
android_media_mediacodec_bufferinfo.constructor);
if (!info_o) {
GST_ERROR ("Failed to call Java method");
(*env)->ExceptionClear (env);
goto done;
}
ret = AMMC_CALL (goto error, Int, dequeueOutputBuffer, info_o, timeoutUs);
if (!_fill_buffer_info (env, info_o, info))
goto error;
done:
if (info_o)
(*env)->DeleteLocalRef (env, info_o);
info_o = NULL;
return ret;
error:
ret = G_MININT;
goto done;
}
gboolean
gst_am_mediacodec_queue_input_buffer (GstAmMediaCodec * self, gint index,
const GstAmmcBufferInfo * info)
{
JNIEnv *env = gst_dvm_get_env ();
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (info != NULL, FALSE);
AMMC_CALL (return FALSE, Void, queueInputBuffer, index, info->offset,
info->size, info->presentation_time_us, info->flags);
return TRUE;
}
gboolean
gst_am_mediacodec_release_output_buffer (GstAmMediaCodec * self, gint index)
{
JNIEnv *env = gst_dvm_get_env ();
g_return_val_if_fail (self != NULL, FALSE);
AMMC_CALL (return FALSE, Void, releaseOutputBuffer, index, JNI_FALSE);
return TRUE;
}