From d0ebf261ffe5204b5f0ede2d6a4579d6c1c8d362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 16 Aug 2012 15:18:19 +0200 Subject: [PATCH] Implement basic wrapper around MediaCodec API Ideally nothing else will be needed from the elements later. --- sys/androidmedia/gstamc-constants.h | 192 ++++ sys/androidmedia/gstamc.c | 1505 ++++++++++++++++++++++++++- sys/androidmedia/gstamc.h | 52 +- 3 files changed, 1706 insertions(+), 43 deletions(-) create mode 100644 sys/androidmedia/gstamc-constants.h diff --git a/sys/androidmedia/gstamc-constants.h b/sys/androidmedia/gstamc-constants.h new file mode 100644 index 0000000000..47c5615f8d --- /dev/null +++ b/sys/androidmedia/gstamc-constants.h @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2012, Collabora Ltd. + * Author: Sebastian Dröge , Collabora Ltd. + * + * 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_CONSTANTS_H__ +#define __GST_AMC_CONSTANTS_H__ + +/* FIXME: We might need to get these values from Java if there's + * ever a device or Android version that changes these values + */ + +/* Copies from MediaCodec.java */ + +enum +{ + BUFFER_FLAG_SYNC_FRAME = 1, + BUFFER_FLAG_CODEC_CONFIG = 2, + BUFFER_FLAG_END_OF_STREAM = 4 +}; + +enum +{ + CONFIGURE_FLAG_ENCODE = 1 +}; + +enum +{ + INFO_TRY_AGAIN_LATER = -1, + INFO_OUTPUT_FORMAT_CHANGED = -2, + INFO_OUTPUT_BUFFERS_CHANGED = -3 +}; + +/* Copies from MediaCodecInfo.java */ +enum +{ + COLOR_FormatMonochrome = 1, + COLOR_Format8bitRGB332 = 2, + COLOR_Format12bitRGB444 = 3, + COLOR_Format16bitARGB4444 = 4, + COLOR_Format16bitARGB1555 = 5, + COLOR_Format16bitRGB565 = 6, + COLOR_Format16bitBGR565 = 7, + COLOR_Format18bitRGB666 = 8, + COLOR_Format18bitARGB1665 = 9, + COLOR_Format19bitARGB1666 = 10, + COLOR_Format24bitRGB888 = 11, + COLOR_Format24bitBGR888 = 12, + COLOR_Format24bitARGB1887 = 13, + COLOR_Format25bitARGB1888 = 14, + COLOR_Format32bitBGRA8888 = 15, + COLOR_Format32bitARGB8888 = 16, + COLOR_FormatYUV411Planar = 17, + COLOR_FormatYUV411PackedPlanar = 18, + COLOR_FormatYUV420Planar = 19, + COLOR_FormatYUV420PackedPlanar = 20, + COLOR_FormatYUV420SemiPlanar = 21, + COLOR_FormatYUV422Planar = 22, + COLOR_FormatYUV422PackedPlanar = 23, + COLOR_FormatYUV422SemiPlanar = 24, + COLOR_FormatYCbYCr = 25, + COLOR_FormatYCrYCb = 26, + COLOR_FormatCbYCrY = 27, + COLOR_FormatCrYCbY = 28, + COLOR_FormatYUV444Interleaved = 29, + COLOR_FormatRawBayer8bit = 30, + COLOR_FormatRawBayer10bit = 31, + COLOR_FormatRawBayer8bitcompressed = 32, + COLOR_FormatL2 = 33, + COLOR_FormatL4 = 34, + COLOR_FormatL8 = 35, + COLOR_FormatL16 = 36 +}; + +enum +{ + AVCProfileBaseline = 0x01, + AVCProfileMain = 0x02, + AVCProfileExtended = 0x04, + AVCProfileHigh = 0x08, + AVCProfileHigh10 = 0x10, + AVCProfileHigh422 = 0x20, + AVCProfileHigh444 = 0x40 +}; + +enum +{ + AVCLevel1 = 0x01, + AVCLevel1b = 0x02, + AVCLevel11 = 0x04, + AVCLevel12 = 0x08, + AVCLevel13 = 0x10, + AVCLevel2 = 0x20, + AVCLevel21 = 0x40, + AVCLevel22 = 0x80, + AVCLevel3 = 0x100, + AVCLevel31 = 0x200, + AVCLevel32 = 0x400, + AVCLevel4 = 0x800, + AVCLevel41 = 0x1000, + AVCLevel42 = 0x2000, + AVCLevel5 = 0x4000, + AVCLevel51 = 0x8000 +}; + +enum +{ + H263ProfileBaseline = 0x01, + H263ProfileH320Coding = 0x02, + H263ProfileBackwardCompatible = 0x04, + H263ProfileISWV2 = 0x08, + H263ProfileISWV3 = 0x10, + H263ProfileHighCompression = 0x20, + H263ProfileInternet = 0x40, + H263ProfileInterlace = 0x80, + H263ProfileHighLatency = 0x100 +}; + +enum +{ + H263Level10 = 0x01, + H263Level20 = 0x02, + H263Level30 = 0x04, + H263Level40 = 0x08, + H263Level45 = 0x10, + H263Level50 = 0x20, + H263Level60 = 0x40, + H263Level70 = 0x80 +}; + +enum +{ + MPEG4ProfileSimple = 0x01, + MPEG4ProfileSimpleScalable = 0x02, + MPEG4ProfileCore = 0x04, + MPEG4ProfileMain = 0x08, + MPEG4ProfileNbit = 0x10, + MPEG4ProfileScalableTexture = 0x20, + MPEG4ProfileSimpleFace = 0x40, + MPEG4ProfileSimpleFBA = 0x80, + MPEG4ProfileBasicAnimated = 0x100, + MPEG4ProfileHybrid = 0x200, + MPEG4ProfileAdvancedRealTime = 0x400, + MPEG4ProfileCoreScalable = 0x800, + MPEG4ProfileAdvancedCoding = 0x1000, + MPEG4ProfileAdvancedCore = 0x2000, + MPEG4ProfileAdvancedScalable = 0x4000, + MPEG4ProfileAdvancedSimple = 0x8000 +}; + +enum +{ + MPEG4Level0 = 0x01, + MPEG4Level0b = 0x02, + MPEG4Level1 = 0x04, + MPEG4Level2 = 0x08, + MPEG4Level3 = 0x10, + MPEG4Level4 = 0x20, + MPEG4Level4a = 0x40, + MPEG4Level5 = 0x80 +}; + +enum +{ + AACObjectMain = 1, + AACObjectLC = 2, + AACObjectSSR = 3, + AACObjectLTP = 4, + AACObjectHE = 5, + AACObjectScalable = 6, + AACObjectERLC = 17, + AACObjectLD = 23, + AACObjectHE_PS = 29, + AACObjectELD = 39 +}; + +#endif diff --git a/sys/androidmedia/gstamc.c b/sys/androidmedia/gstamc.c index 5c8be9097f..dab645a05f 100644 --- a/sys/androidmedia/gstamc.c +++ b/sys/androidmedia/gstamc.c @@ -23,8 +23,10 @@ #endif #include "gstamc.h" +#include "gstamc-constants.h" #include +#include #include #include @@ -37,6 +39,52 @@ static jint (*get_created_java_vms) (JavaVM ** vmBuf, jsize bufLen, static jint (*create_java_vm) (JavaVM ** p_vm, JNIEnv ** p_env, void *vm_args); static JavaVM *java_vm; +/* 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_output_buffers; + 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 contains_key; + jmethodID get_float; + jmethodID set_float; + jmethodID get_integer; + jmethodID set_integer; + jmethodID get_string; + jmethodID set_string; +} media_format; + static JNIEnv * gst_amc_attach_current_thread (void) { @@ -131,149 +179,1162 @@ create_failed: } } +static jstring +gst_amc_jstring_new (JNIEnv * env, const gchar * chars, jint len) +{ + jcharArray array; + jstring result; + + array = (*env)->NewCharArray (env, len); + if (array == NULL) { + GST_ERROR ("Failed to create char array of length %d", len); + (*env)->ExceptionClear (env); + return NULL; + } + + (*env)->SetCharArrayRegion (env, array, 0, len, (const jchar *) chars); + + result = + (*env)->NewObject (env, java_string.klass, java_string.constructor, + array); + + (*env)->DeleteLocalRef (env, array); + + return result; +} GstAmcCodec * gst_amc_codec_new (const gchar * name) { - return NULL; + JNIEnv *env; + GstAmcCodec *codec; + jstring name_str; + jobject object = NULL; + + g_return_val_if_fail (name != NULL, NULL); + + env = gst_amc_attach_current_thread (); + + name_str = gst_amc_jstring_new (env, name, strlen (name)); + if (name_str == NULL) + goto error; + + codec = g_slice_new0 (GstAmcCodec); + + object = + (*env)->CallStaticObjectMethod (env, media_codec.klass, + media_codec.create_by_codec_name, name_str); + if ((*env)->ExceptionCheck (env) || !object) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to create codec '%s'", codec); + goto error; + } + + codec->object = (*env)->NewGlobalRef (env, object); + if (!codec->object) { + GST_ERROR ("Failed to create global reference"); + (*env)->ExceptionClear (env); + goto error; + } + +done: + if (object) + (*env)->DeleteLocalRef (env, object); + if (name_str) + (*env)->DeleteLocalRef (env, name_str); + name_str = NULL; + gst_amc_detach_current_thread (); + + return codec; + +error: + 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_attach_current_thread (); + (*env)->DeleteGlobalRef (env, codec->object); + g_slice_free (GstAmcCodec, codec); + gst_amc_detach_current_thread (); } -void -gst_amc_codec_configure (GstAmcCodec * codec, gint flags) +gboolean +gst_amc_codec_configure (GstAmcCodec * codec, GstAmcFormat * format, gint flags) { + JNIEnv *env; + gboolean ret = TRUE; + g_return_val_if_fail (codec != NULL, FALSE); + g_return_val_if_fail (format != NULL, FALSE); + + env = gst_amc_attach_current_thread (); + + (*env)->CallVoidMethod (env, codec->object, media_codec.configure, + format->object, flags); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + ret = FALSE; + goto done; + } + +done: + gst_amc_detach_current_thread (); + + return ret; } GstAmcFormat * gst_amc_codec_get_output_format (GstAmcCodec * codec) { - return NULL; + JNIEnv *env; + GstAmcFormat *ret = NULL; + jobject object = NULL; + + g_return_val_if_fail (codec != NULL, NULL); + + env = gst_amc_attach_current_thread (); + + object = + (*env)->CallObjectMethod (env, codec->object, + media_codec.get_output_format); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + goto done; + } + + ret = g_slice_new0 (GstAmcFormat); + + ret->object = (*env)->NewGlobalRef (env, object); + if (!ret->object) { + GST_ERROR ("Failed to create global reference"); + (*env)->ExceptionClear (env); + g_slice_free (GstAmcFormat, ret); + ret = NULL; + } + + (*env)->DeleteLocalRef (env, object); + +done: + gst_amc_detach_current_thread (); + + return ret; } -void +gboolean gst_amc_codec_start (GstAmcCodec * codec) { + JNIEnv *env; + gboolean ret = TRUE; + g_return_val_if_fail (codec != NULL, FALSE); + + env = gst_amc_attach_current_thread (); + + (*env)->CallVoidMethod (env, codec->object, media_codec.start); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + ret = FALSE; + goto done; + } + +done: + gst_amc_detach_current_thread (); + + return ret; } -void +gboolean gst_amc_codec_stop (GstAmcCodec * codec) { + JNIEnv *env; + gboolean ret = TRUE; + g_return_val_if_fail (codec != NULL, FALSE); + + env = gst_amc_attach_current_thread (); + + (*env)->CallVoidMethod (env, codec->object, media_codec.stop); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + ret = FALSE; + goto done; + } + +done: + gst_amc_detach_current_thread (); + + return ret; +} + +gboolean +gst_amc_codec_flush (GstAmcCodec * codec) +{ + JNIEnv *env; + gboolean ret = TRUE; + + g_return_val_if_fail (codec != NULL, FALSE); + + env = gst_amc_attach_current_thread (); + + (*env)->CallVoidMethod (env, codec->object, media_codec.flush); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + ret = FALSE; + goto done; + } + +done: + gst_amc_detach_current_thread (); + + return ret; +} + +gboolean +gst_amc_codec_release (GstAmcCodec * codec) +{ + JNIEnv *env; + gboolean ret = TRUE; + + g_return_val_if_fail (codec != NULL, FALSE); + + env = gst_amc_attach_current_thread (); + + (*env)->CallVoidMethod (env, codec->object, media_codec.release); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + ret = FALSE; + goto done; + } + +done: + gst_amc_detach_current_thread (); + + return ret; } void -gst_amc_codec_flush (GstAmcCodec * codec) +gst_amc_codec_free_buffers (GstAmcBuffer * buffers, gsize n_buffers) { + JNIEnv *env; + jsize i; + g_return_if_fail (buffers != NULL); + + env = gst_amc_attach_current_thread (); + + for (i = 0; i < n_buffers; i++) { + if (buffers[i].object) + (*env)->DeleteGlobalRef (env, buffers[i].object); + } + g_free (buffers); + + gst_amc_detach_current_thread (); } GstAmcBuffer * gst_amc_codec_get_output_buffers (GstAmcCodec * codec, gsize * n_buffers) { - return NULL; + JNIEnv *env; + jobject output_buffers = NULL; + jsize n_output_buffers; + GstAmcBuffer *ret = NULL; + jsize i; + + g_return_val_if_fail (codec != NULL, NULL); + g_return_val_if_fail (n_buffers != NULL, NULL); + + *n_buffers = 0; + env = gst_amc_attach_current_thread (); + + output_buffers = + (*env)->CallObjectMethod (env, codec->object, + media_codec.get_output_buffers); + if ((*env)->ExceptionCheck (env) || !output_buffers) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + 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; + + gst_amc_detach_current_thread (); + + return ret; +error: + if (ret) + gst_amc_codec_free_buffers (ret, n_output_buffers); + ret = NULL; + *n_buffers = 0; + goto done; } GstAmcBuffer * gst_amc_codec_get_input_buffers (GstAmcCodec * codec, gsize * n_buffers) { - return NULL; + JNIEnv *env; + jobject input_buffers = NULL; + jsize n_input_buffers; + GstAmcBuffer *ret = NULL; + jsize i; + + g_return_val_if_fail (codec != NULL, NULL); + g_return_val_if_fail (n_buffers != NULL, NULL); + + *n_buffers = 0; + env = gst_amc_attach_current_thread (); + + input_buffers = + (*env)->CallObjectMethod (env, codec->object, + media_codec.get_input_buffers); + if ((*env)->ExceptionCheck (env) || !input_buffers) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + 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; + + gst_amc_detach_current_thread (); + + return ret; +error: + if (ret) + gst_amc_codec_free_buffers (ret, n_input_buffers); + ret = NULL; + *n_buffers = 0; + goto done; } gint gst_amc_codec_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs) { - return -1; + JNIEnv *env; + gint ret = G_MININT; + + g_return_val_if_fail (codec != NULL, FALSE); + + env = gst_amc_attach_current_thread (); + + ret = + (*env)->CallIntMethod (env, codec->object, + media_codec.dequeue_input_buffer, timeoutUs); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + ret = G_MININT; + goto done; + } + +done: + gst_amc_detach_current_thread (); + + return ret; +} + +static gboolean +gst_amc_codec_fill_buffer_info (JNIEnv * env, jobject buffer_info, + GstAmcBufferInfo * info) +{ + g_return_val_if_fail (buffer_info != NULL, FALSE); + + info->flags = + (*env)->GetIntField (env, buffer_info, media_codec_buffer_info.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, media_codec_buffer_info.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, + media_codec_buffer_info.presentation_time_us); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get buffer info field"); + return FALSE; + } + + info->size = + (*env)->GetIntField (env, buffer_info, media_codec_buffer_info.size); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get buffer info field"); + return FALSE; + } + + return TRUE; } gint gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec, GstAmcBufferInfo * info, gint64 timeoutUs) { - return -1; + JNIEnv *env; + gint ret = G_MININT; + jobject info_o = NULL; + + g_return_val_if_fail (codec != NULL, FALSE); + + env = gst_amc_attach_current_thread (); + + info_o = + (*env)->NewObject (env, media_codec_buffer_info.klass, + media_codec_buffer_info.constructor); + if (!info_o) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + goto done; + } + + ret = + (*env)->CallIntMethod (env, codec->object, + media_codec.dequeue_output_buffer, info_o, timeoutUs); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + ret = G_MININT; + goto done; + } + + if (!gst_amc_codec_fill_buffer_info (env, info_o, info)) { + ret = G_MININT; + goto done; + } + +done: + if (info_o) + (*env)->DeleteLocalRef (env, info_o); + info_o = NULL; + + gst_amc_detach_current_thread (); + + return ret; } -void +gboolean gst_amc_codec_queue_input_buffer (GstAmcCodec * codec, gint index, const GstAmcBufferInfo * info) { + JNIEnv *env; + gboolean ret = TRUE; + g_return_val_if_fail (codec != NULL, FALSE); + g_return_val_if_fail (info != NULL, FALSE); + + env = gst_amc_attach_current_thread (); + + (*env)->CallVoidMethod (env, codec->object, media_codec.queue_input_buffer, + index, info->offset, info->size, info->presentation_time_us, info->flags); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + ret = FALSE; + goto done; + } + +done: + gst_amc_detach_current_thread (); + + return ret; } -void +gboolean gst_amc_codec_release_output_buffer (GstAmcCodec * codec, gint index) { + JNIEnv *env; + gboolean ret = TRUE; + g_return_val_if_fail (codec != NULL, FALSE); + + env = gst_amc_attach_current_thread (); + + (*env)->CallVoidMethod (env, codec->object, media_codec.release_output_buffer, + index, JNI_FALSE); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + ret = FALSE; + goto done; + } + +done: + gst_amc_detach_current_thread (); + + return ret; } GstAmcFormat * gst_amc_format_new_audio (const gchar * mime, gint sample_rate, gint channels) { - return NULL; + JNIEnv *env; + GstAmcFormat *format; + jstring mime_str; + jobject object = NULL; + + g_return_val_if_fail (mime != NULL, NULL); + + env = gst_amc_attach_current_thread (); + + mime_str = gst_amc_jstring_new (env, mime, strlen (mime)); + if (mime_str == NULL) + goto error; + + format = g_slice_new0 (GstAmcFormat); + + object = + (*env)->CallStaticObjectMethod (env, media_format.klass, + media_format.create_audio_format, mime_str, sample_rate, channels); + if ((*env)->ExceptionCheck (env) || !object) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to create format '%s'", mime); + goto error; + } + + format->object = (*env)->NewGlobalRef (env, object); + if (!format->object) { + GST_ERROR ("Failed to create global reference"); + (*env)->ExceptionClear (env); + goto error; + } + +done: + if (object) + (*env)->DeleteLocalRef (env, object); + if (mime_str) + (*env)->DeleteLocalRef (env, mime_str); + mime_str = NULL; + gst_amc_detach_current_thread (); + + return format; + +error: + g_slice_free (GstAmcFormat, format); + format = NULL; + goto done; } GstAmcFormat * gst_amc_format_new_video (const gchar * mime, gint width, gint height) { - return NULL; + JNIEnv *env; + GstAmcFormat *format; + jstring mime_str; + jobject object = NULL; + + g_return_val_if_fail (mime != NULL, NULL); + + env = gst_amc_attach_current_thread (); + + mime_str = gst_amc_jstring_new (env, mime, strlen (mime)); + if (mime_str == NULL) + goto error; + + format = g_slice_new0 (GstAmcFormat); + + object = + (*env)->CallStaticObjectMethod (env, media_format.klass, + media_format.create_video_format, mime_str, width, height); + if ((*env)->ExceptionCheck (env) || !object) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to create format '%s'", mime); + goto error; + } + + format->object = (*env)->NewGlobalRef (env, object); + if (!format->object) { + GST_ERROR ("Failed to create global reference"); + (*env)->ExceptionClear (env); + goto error; + } + +done: + if (object) + (*env)->DeleteLocalRef (env, object); + if (mime_str) + (*env)->DeleteLocalRef (env, mime_str); + mime_str = NULL; + gst_amc_detach_current_thread (); + + return format; + +error: + 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_attach_current_thread (); + (*env)->DeleteGlobalRef (env, format->object); + g_slice_free (GstAmcFormat, format); + gst_amc_detach_current_thread (); } gboolean gst_amc_format_contains_key (GstAmcFormat * format, const gchar * key) { - return FALSE; + 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_attach_current_thread (); + + key_str = gst_amc_jstring_new (env, key, strlen (key)); + if (!key_str) + goto done; + + ret = + (*env)->CallBooleanMethod (env, format->object, media_format.contains_key, + key_str); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + goto done; + } + +done: + if (key_str) + (*env)->DeleteLocalRef (env, key_str); + gst_amc_detach_current_thread (); + + return ret; } gboolean gst_amc_format_get_float (GstAmcFormat * format, const gchar * key, gfloat * value) { - return FALSE; + 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_attach_current_thread (); + + key_str = gst_amc_jstring_new (env, key, strlen (key)); + if (!key_str) + goto done; + + *value = + (*env)->CallFloatMethod (env, format->object, media_format.get_float); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + goto done; + } + ret = TRUE; + +done: + if (key_str) + (*env)->DeleteLocalRef (env, key_str); + gst_amc_detach_current_thread (); + + return ret; } void gst_amc_format_set_float (GstAmcFormat * format, const gchar * key, - gfloat * value) + gfloat value) { + JNIEnv *env; + jstring key_str = NULL; + g_return_if_fail (format != NULL); + g_return_if_fail (key != NULL); + + env = gst_amc_attach_current_thread (); + + key_str = gst_amc_jstring_new (env, key, strlen (key)); + if (!key_str) + goto done; + + (*env)->CallVoidMethod (env, format->object, media_format.set_float, value); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + goto done; + } + +done: + if (key_str) + (*env)->DeleteLocalRef (env, key_str); + gst_amc_detach_current_thread (); } gboolean gst_amc_format_get_int (GstAmcFormat * format, const gchar * key, gint * value) { - return FALSE; + 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_attach_current_thread (); + + key_str = gst_amc_jstring_new (env, key, strlen (key)); + if (!key_str) + goto done; + + *value = + (*env)->CallIntMethod (env, format->object, media_format.get_integer); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + goto done; + } + ret = TRUE; + +done: + if (key_str) + (*env)->DeleteLocalRef (env, key_str); + gst_amc_detach_current_thread (); + + return ret; + } void -gst_amc_format_set_int (GstAmcFormat * format, const gchar * key, gint * value) +gst_amc_format_set_int (GstAmcFormat * format, const gchar * key, gint value) { + JNIEnv *env; + jstring key_str = NULL; + g_return_if_fail (format != NULL); + g_return_if_fail (key != NULL); + + env = gst_amc_attach_current_thread (); + + key_str = gst_amc_jstring_new (env, key, strlen (key)); + if (!key_str) + goto done; + + (*env)->CallVoidMethod (env, format->object, media_format.set_integer, value); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + goto done; + } + +done: + if (key_str) + (*env)->DeleteLocalRef (env, key_str); + gst_amc_detach_current_thread (); } gboolean gst_amc_format_get_string (GstAmcFormat * format, const gchar * key, gchar ** value) { - return FALSE; + JNIEnv *env; + gboolean ret = FALSE; + jstring key_str = NULL; + jstring v_str = NULL; + const gchar *v = 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_attach_current_thread (); + + key_str = gst_amc_jstring_new (env, key, strlen (key)); + if (!key_str) + goto done; + + v_str = + (*env)->CallObjectMethod (env, format->object, media_format.get_string); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + goto done; + } + + v = (*env)->GetStringUTFChars (env, v_str, NULL); + if (!v) { + GST_ERROR ("Failed to convert string to UTF8"); + (*env)->ExceptionClear (env); + goto done; + } + + *value = g_strdup (v); + + ret = TRUE; + +done: + if (key_str) + (*env)->DeleteLocalRef (env, key_str); + if (v) + (*env)->ReleaseStringUTFChars (env, v_str, v); + if (v_str) + (*env)->DeleteLocalRef (env, v_str); + gst_amc_detach_current_thread (); + + return ret; } void gst_amc_format_set_string (GstAmcFormat * format, const gchar * key, const gchar * value) { + JNIEnv *env; + jstring key_str = NULL; + jstring v_str = NULL; + g_return_if_fail (format != NULL); + g_return_if_fail (key != NULL); + g_return_if_fail (value != NULL); + + env = gst_amc_attach_current_thread (); + + key_str = gst_amc_jstring_new (env, key, strlen (key)); + if (!key_str) + goto done; + + v_str = gst_amc_jstring_new (env, value, strlen (value)); + if (!v_str) + goto done; + + (*env)->CallVoidMethod (env, format->object, media_format.set_string, v_str); + if ((*env)->ExceptionCheck (env)) { + GST_ERROR ("Failed to call Java method"); + (*env)->ExceptionClear (env); + goto done; + } + +done: + if (key_str) + (*env)->DeleteLocalRef (env, key_str); + if (v_str) + (*env)->DeleteLocalRef (env, v_str); + gst_amc_detach_current_thread (); +} + +static gboolean +get_java_classes (void) +{ + gboolean ret = TRUE; + JNIEnv *env; + jclass tmp; + + env = gst_amc_attach_current_thread (); + + tmp = (*env)->FindClass (env, "java/lang/String"); + if (!tmp) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get string class"); + goto done; + } + java_string.klass = (*env)->NewGlobalRef (env, tmp); + if (!java_string.klass) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get string class global reference"); + goto done; + } + (*env)->DeleteLocalRef (env, tmp); + tmp = NULL; + + java_string.constructor = + (*env)->GetMethodID (env, java_string.klass, "", "([C)V"); + if (!java_string.constructor) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get string methods"); + goto done; + } + + tmp = (*env)->FindClass (env, "android/media/MediaCodec"); + if (!tmp) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get codec class"); + goto done; + } + media_codec.klass = (*env)->NewGlobalRef (env, tmp); + if (!media_codec.klass) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get codec class global reference"); + 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; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get codec methods"); + goto done; + } + + 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; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get codec buffer info class global reference"); + 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; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get buffer info methods and fields"); + goto done; + } + + tmp = (*env)->FindClass (env, "android/media/MediaFormat"); + if (!tmp) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get format class"); + goto done; + } + media_format.klass = (*env)->NewGlobalRef (env, tmp); + if (!media_format.klass) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get format class global reference"); + 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.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"); + 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) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get format methods"); + goto done; + } + +done: + if (tmp) + (*env)->DeleteLocalRef (env, tmp); + tmp = NULL; + gst_amc_detach_current_thread (); + + return ret; } static GList *codec_infos = NULL; @@ -332,7 +1393,7 @@ register_codecs (void) jarray supported_types = NULL; jsize n_supported_types; jsize j; - gboolean valid_type = FALSE; + gboolean valid_codec = TRUE; gst_codec_info = g_new0 (GstAmcCodecInfo, 1); @@ -342,6 +1403,7 @@ register_codecs (void) if ((*env)->ExceptionCheck (env) || !codec_info) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get codec info %d", i); + valid_codec = FALSE; goto next_codec; } @@ -349,6 +1411,7 @@ register_codecs (void) if (!codec_list_class) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get codec info class"); + valid_codec = FALSE; goto next_codec; } @@ -367,6 +1430,7 @@ register_codecs (void) || !get_supported_types_id || !is_encoder_id) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get codec info method IDs"); + valid_codec = FALSE; goto next_codec; } @@ -374,12 +1438,14 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get codec name"); + valid_codec = FALSE; goto next_codec; } name_str = (*env)->GetStringUTFChars (env, name, NULL); if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to convert codec name to UTF8"); + valid_codec = FALSE; goto next_codec; } @@ -389,6 +1455,7 @@ register_codecs (void) if (strcmp (name_str, "AACEncoder") == 0 || strcmp (name_str, "OMX.google.raw.decoder") == 0) { GST_INFO ("Skipping compatibility codec '%s'", name_str); + valid_codec = FALSE; goto next_codec; } gst_codec_info->name = g_strdup (name_str); @@ -397,6 +1464,7 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to detect if codec is an encoder"); + valid_codec = FALSE; goto next_codec; } gst_codec_info->is_encoder = is_encoder; @@ -406,6 +1474,7 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get supported types"); + valid_codec = FALSE; goto next_codec; } @@ -413,6 +1482,7 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get supported types array length"); + valid_codec = FALSE; goto next_codec; } @@ -421,6 +1491,12 @@ register_codecs (void) gst_codec_info->supported_types = g_new0 (GstAmcCodecType, 1); gst_codec_info->n_supported_types = n_supported_types; + if (n_supported_types == 0) { + valid_codec = FALSE; + GST_ERROR ("Codec has no supported types"); + goto next_codec; + } + for (j = 0; j < n_supported_types; j++) { GstAmcCodecType *gst_codec_type; jobject supported_type = NULL; @@ -439,6 +1515,7 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get %d-th supported type", j); + valid_codec = FALSE; goto next_supported_type; } @@ -447,12 +1524,12 @@ register_codecs (void) if ((*env)->ExceptionCheck (env) || !supported_type_str) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to convert supported type to UTF8"); + valid_codec = FALSE; goto next_supported_type; } GST_INFO ("Supported type '%s'", supported_type_str); gst_codec_type->mime = g_strdup (supported_type_str); - valid_type = TRUE; capabilities = (*env)->CallObjectMethod (env, codec_info, @@ -460,6 +1537,7 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get capabilities for supported type"); + valid_codec = FALSE; goto next_supported_type; } @@ -467,6 +1545,7 @@ register_codecs (void) if (!capabilities_class) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get capabilities class"); + valid_codec = FALSE; goto next_supported_type; } @@ -478,6 +1557,7 @@ register_codecs (void) if (!color_formats_id || !profile_levels_id) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get capabilities field IDs"); + valid_codec = FALSE; goto next_supported_type; } @@ -486,10 +1566,17 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get color formats"); + valid_codec = FALSE; goto next_supported_type; } n_elems = (*env)->GetArrayLength (env, color_formats); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get color formats array length"); + valid_codec = FALSE; + goto next_supported_type; + } gst_codec_type->n_color_formats = n_elems; gst_codec_type->color_formats = g_new0 (gint, n_elems); color_formats_elems = @@ -497,6 +1584,7 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get color format elements"); + valid_codec = FALSE; goto next_supported_type; } @@ -510,10 +1598,17 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get profile/levels"); + valid_codec = FALSE; goto next_supported_type; } n_elems = (*env)->GetArrayLength (env, profile_levels); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get profile/levels array length"); + valid_codec = FALSE; + goto next_supported_type; + } gst_codec_type->n_profile_levels = n_elems; gst_codec_type->profile_levels = g_malloc0 (sizeof (gst_codec_type->profile_levels) * n_elems); @@ -527,6 +1622,7 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get %d-th profile/level", k); + valid_codec = FALSE; goto next_profile_level; } @@ -534,6 +1630,7 @@ register_codecs (void) if (!profile_level_class) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get profile/level class"); + valid_codec = FALSE; goto next_profile_level; } @@ -543,6 +1640,7 @@ register_codecs (void) if (!level_id || !profile_id) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get profile/level field IDs"); + valid_codec = FALSE; goto next_profile_level; } @@ -550,6 +1648,7 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get level"); + valid_codec = FALSE; goto next_profile_level; } GST_INFO ("Level %d: 0x%08x", k, level); @@ -559,6 +1658,7 @@ register_codecs (void) if ((*env)->ExceptionCheck (env)) { (*env)->ExceptionClear (env); GST_ERROR ("Failed to get profile"); + valid_codec = FALSE; goto next_profile_level; } GST_INFO ("Profile %d: 0x%08x", k, profile); @@ -571,6 +1671,8 @@ register_codecs (void) if (profile_level_class) (*env)->DeleteLocalRef (env, profile_level_class); profile_level_class = NULL; + if (!valid_codec) + break; } next_supported_type: @@ -596,10 +1698,12 @@ register_codecs (void) if (supported_type) (*env)->DeleteLocalRef (env, supported_type); supported_type = NULL; + if (!valid_codec) + break; } /* We need at least a valid supported type */ - if (valid_type) { + if (valid_codec) { GST_LOG ("Successfully scanned codec '%s'", name_str); codec_infos = g_list_append (codec_infos, gst_codec_info); gst_codec_info = NULL; @@ -637,7 +1741,7 @@ register_codecs (void) g_free (gst_codec_info); } gst_codec_info = NULL; - valid_type = FALSE; + valid_codec = TRUE; } ret = codec_infos != NULL; @@ -651,6 +1755,355 @@ done: return ret; } +static const struct +{ + gint color_format; + GstVideoFormat video_format; +} color_format_mapping_table[] = { + { + COLOR_FormatYUV420Planar, GST_VIDEO_FORMAT_I420} +}; + +GstVideoFormat +gst_amc_color_format_to_video_format (gint color_format) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (color_format_mapping_table); i++) { + if (color_format_mapping_table[i].color_format == color_format) + return color_format_mapping_table[i].video_format; + } + + return GST_VIDEO_FORMAT_UNKNOWN; +} + +gint +gst_amc_video_format_to_color_format (GstVideoFormat video_format) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (color_format_mapping_table); i++) { + if (color_format_mapping_table[i].video_format == video_format) + return color_format_mapping_table[i].color_format; + } + + return -1; +} + +static const struct +{ + gint id; + const gchar *str; +} avc_profile_mapping_table[] = { + { + AVCProfileBaseline, "baseline"}, { + AVCProfileMain, "main"}, { + AVCProfileExtended, "extended"}, { + AVCProfileHigh, "high"}, { + AVCProfileHigh10, "high-10"}, { + AVCProfileHigh422, "high-4:2:2"}, { + AVCProfileHigh444, "high-4:4:4"} +}; + +const gchar * +gst_amc_avc_profile_to_string (gint profile) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (avc_profile_mapping_table); i++) { + if (avc_profile_mapping_table[i].id == profile) + return avc_profile_mapping_table[i].str; + } + + return NULL; +} + +gint +gst_amc_avc_profile_from_string (const gchar * profile) +{ + gint i; + + g_return_val_if_fail (profile != NULL, -1); + + for (i = 0; i < G_N_ELEMENTS (avc_profile_mapping_table); i++) { + if (strcmp (avc_profile_mapping_table[i].str, profile) == 0) + return avc_profile_mapping_table[i].id; + } + + return -1; +} + +static const struct +{ + gint id; + const gchar *str; +} avc_level_mapping_table[] = { + { + AVCLevel1, "1"}, { + AVCLevel1b, "1b"}, { + AVCLevel11, "1.1"}, { + AVCLevel12, "1.2"}, { + AVCLevel13, "1.3"}, { + AVCLevel2, "2"}, { + AVCLevel21, "2.1"}, { + AVCLevel22, "2.2"}, { + AVCLevel3, "3"}, { + AVCLevel31, "3.1"}, { + AVCLevel32, "3.2"}, { + AVCLevel4, "4"}, { + AVCLevel41, "4.1"}, { + AVCLevel42, "4.2"}, { + AVCLevel5, "5"}, { + AVCLevel51, "5.1"} +}; + +const gchar * +gst_amc_avc_level_to_string (gint level) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (avc_level_mapping_table); i++) { + if (avc_level_mapping_table[i].id == level) + return avc_level_mapping_table[i].str; + } + + return NULL; +} + +gint +gst_amc_avc_level_from_string (const gchar * level) +{ + gint i; + + g_return_val_if_fail (level != NULL, -1); + + for (i = 0; i < G_N_ELEMENTS (avc_level_mapping_table); i++) { + if (strcmp (avc_level_mapping_table[i].str, level) == 0) + return avc_level_mapping_table[i].id; + } + + return -1; +} + +static const struct +{ + gint id; + gint gst_id; +} h263_profile_mapping_table[] = { + { + H263ProfileBaseline, 0}, { + H263ProfileH320Coding, 1}, { + H263ProfileBackwardCompatible, 2}, { + H263ProfileISWV2, 3}, { + H263ProfileISWV3, 4}, { + H263ProfileHighCompression, 5}, { + H263ProfileInternet, 6}, { + H263ProfileInterlace, 7}, { + H263ProfileHighLatency, 8} +}; + +gint +gst_amc_h263_profile_to_gst_id (gint profile) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (h263_profile_mapping_table); i++) { + if (h263_profile_mapping_table[i].id == profile) + return h263_profile_mapping_table[i].gst_id; + } + + return -1; +} + +gint +gst_amc_h263_profile_from_gst_id (gint profile) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (h263_profile_mapping_table); i++) { + if (h263_profile_mapping_table[i].gst_id == profile) + return h263_profile_mapping_table[i].id; + } + + return -1; +} + +static const struct +{ + gint id; + gint gst_id; +} h263_level_mapping_table[] = { + { + H263Level10, 10}, { + H263Level20, 20}, { + H263Level30, 30}, { + H263Level40, 40}, { + H263Level50, 50}, { + H263Level60, 60}, { + H263Level70, 70} +}; + +gint +gst_amc_h263_level_to_gst_id (gint level) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (h263_level_mapping_table); i++) { + if (h263_level_mapping_table[i].id == level) + return h263_level_mapping_table[i].gst_id; + } + + return -1; +} + +gint +gst_amc_h263_level_from_gst_id (gint level) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (h263_level_mapping_table); i++) { + if (h263_level_mapping_table[i].gst_id == level) + return h263_level_mapping_table[i].id; + } + + return -1; +} + +static const struct +{ + gint id; + const gchar *str; +} mpeg4_profile_mapping_table[] = { + { + MPEG4ProfileSimple, "simple"}, { + MPEG4ProfileSimpleScalable, "simple-scalable"}, { + MPEG4ProfileCore, "core"}, { + MPEG4ProfileMain, "main"}, { + MPEG4ProfileNbit, "n-bit"}, { + MPEG4ProfileScalableTexture, "scalable"}, { + MPEG4ProfileSimpleFace, "simple-face"}, { + MPEG4ProfileSimpleFBA, "simple-fba"}, { + MPEG4ProfileBasicAnimated, "basic-animated-texture"}, { + MPEG4ProfileHybrid, "hybrid"}, { + MPEG4ProfileAdvancedRealTime, "advanced-real-time"}, { + MPEG4ProfileCoreScalable, "core-scalable"}, { + MPEG4ProfileAdvancedCoding, "advanced-coding-efficiency"}, { + MPEG4ProfileAdvancedCore, "advanced-core"}, { + MPEG4ProfileAdvancedScalable, "advanced-scalable-texture"}, { + MPEG4ProfileAdvancedSimple, "advanced-simple"} +}; + +const gchar * +gst_amc_mpeg4_profile_to_string (gint profile) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (mpeg4_profile_mapping_table); i++) { + if (mpeg4_profile_mapping_table[i].id == profile) + return mpeg4_profile_mapping_table[i].str; + } + + return NULL; +} + +gint +gst_amc_avc_mpeg4_profile_from_string (const gchar * profile) +{ + gint i; + + g_return_val_if_fail (profile != NULL, -1); + + for (i = 0; i < G_N_ELEMENTS (mpeg4_profile_mapping_table); i++) { + if (strcmp (mpeg4_profile_mapping_table[i].str, profile) == 0) + return mpeg4_profile_mapping_table[i].id; + } + + return -1; +} + +static const struct +{ + gint id; + const gchar *str; +} mpeg4_level_mapping_table[] = { + { + MPEG4Level0, "0"}, { + MPEG4Level0b, "0b"}, { + MPEG4Level1, "1"}, { + MPEG4Level2, "2"}, { + MPEG4Level3, "3"}, { + MPEG4Level4, "4"}, { + MPEG4Level4a, "4a"}, { +MPEG4Level5, "5"},}; + +const gchar * +gst_amc_mpeg4_level_to_string (gint level) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (mpeg4_level_mapping_table); i++) { + if (mpeg4_level_mapping_table[i].id == level) + return mpeg4_level_mapping_table[i].str; + } + + return NULL; +} + +gint +gst_amc_mpeg4_level_from_string (const gchar * level) +{ + gint i; + + g_return_val_if_fail (level != NULL, -1); + + for (i = 0; i < G_N_ELEMENTS (mpeg4_level_mapping_table); i++) { + if (strcmp (mpeg4_level_mapping_table[i].str, level) == 0) + return mpeg4_level_mapping_table[i].id; + } + + return -1; +} + +static const struct +{ + gint id; + const gchar *str; +} aac_profile_mapping_table[] = { + { + AACObjectMain, "main"}, { + AACObjectLC, "lc"}, { + AACObjectSSR, "ssr"}, { + AACObjectLTP, "ltp"} +}; + +const gchar * +gst_amc_aac_profile_to_string (gint profile) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (aac_profile_mapping_table); i++) { + if (aac_profile_mapping_table[i].id == profile) + return aac_profile_mapping_table[i].str; + } + + return NULL; +} + +gint +gst_amc_aac_profile_from_string (const gchar * profile) +{ + gint i; + + g_return_val_if_fail (profile != NULL, -1); + + for (i = 0; i < G_N_ELEMENTS (aac_profile_mapping_table); i++) { + if (strcmp (aac_profile_mapping_table[i].str, profile) == 0) + return aac_profile_mapping_table[i].id; + } + + return -1; +} + static gboolean plugin_init (GstPlugin * plugin) { @@ -659,7 +2112,7 @@ plugin_init (GstPlugin * plugin) if (!initialize_java_vm ()) return FALSE; - if (!register_codecs ()) + if (!get_java_classes () || !register_codecs ()) return FALSE; return TRUE; diff --git a/sys/androidmedia/gstamc.h b/sys/androidmedia/gstamc.h index 7c6398f25f..c473a138c3 100644 --- a/sys/androidmedia/gstamc.h +++ b/sys/androidmedia/gstamc.h @@ -22,6 +22,7 @@ #define __GST_AMC_H__ #include +#include #include G_BEGIN_DECLS @@ -29,7 +30,7 @@ G_BEGIN_DECLS typedef struct _GstAmcCodecInfo GstAmcCodecInfo; typedef struct _GstAmcCodecType GstAmcCodecType; typedef struct _GstAmcCodec GstAmcCodec; -typedef struct _GstAmcCodecBufferInfo GstAmcBufferInfo; +typedef struct _GstAmcBufferInfo GstAmcBufferInfo; typedef struct _GstAmcFormat GstAmcFormat; typedef struct _GstAmcBuffer GstAmcBuffer; @@ -54,49 +55,48 @@ struct _GstAmcCodecInfo { }; struct _GstAmcBuffer { + jobject object; /* global reference */ guint8 *data; - gsize len; + gsize size; }; struct _GstAmcFormat { /* < private > */ - jobject format; /* global reference */ - jclass format_class; /* global reference */ + jobject object; /* global reference */ }; struct _GstAmcCodec { /* < private > */ - jobject codec; /* global reference */ - jclass codec_class; /* global reference */ + jobject object; /* global reference */ }; struct _GstAmcBufferInfo { gint flags; gint offset; - gint64 presentationTimeUs; + gint64 presentation_time_us; gint size; }; - - GstAmcCodec * gst_amc_codec_new (const gchar *name); void gst_amc_codec_free (GstAmcCodec * codec); -void gst_amc_codec_configure (GstAmcCodec * codec, gint flags); +gboolean gst_amc_codec_configure (GstAmcCodec * codec, GstAmcFormat * format, gint flags); GstAmcFormat * gst_amc_codec_get_output_format (GstAmcCodec * codec); -void gst_amc_codec_start (GstAmcCodec * codec); -void gst_amc_codec_stop (GstAmcCodec * codec); -void gst_amc_codec_flush (GstAmcCodec * codec); +gboolean gst_amc_codec_start (GstAmcCodec * codec); +gboolean gst_amc_codec_stop (GstAmcCodec * codec); +gboolean gst_amc_codec_flush (GstAmcCodec * codec); +gboolean gst_amc_codec_release (GstAmcCodec * codec); GstAmcBuffer * gst_amc_codec_get_output_buffers (GstAmcCodec * codec, gsize * n_buffers); GstAmcBuffer * gst_amc_codec_get_input_buffers (GstAmcCodec * codec, gsize * n_buffers); +void gst_amc_codec_free_buffers (GstAmcBuffer * buffers, gsize n_buffers); gint gst_amc_codec_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs); gint gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec, GstAmcBufferInfo *info, gint64 timeoutUs); -void gst_amc_codec_queue_input_buffer (GstAmcCodec * codec, gint index, const GstAmcBufferInfo *info); -void gst_amc_codec_release_output_buffer (GstAmcCodec * codec, gint index); +gboolean gst_amc_codec_queue_input_buffer (GstAmcCodec * codec, gint index, const GstAmcBufferInfo *info); +gboolean gst_amc_codec_release_output_buffer (GstAmcCodec * codec, gint index); GstAmcFormat * gst_amc_format_new_audio (const gchar *mime, gint sample_rate, gint channels); @@ -106,12 +106,30 @@ void gst_amc_format_free (GstAmcFormat * format); gboolean gst_amc_format_contains_key (GstAmcFormat *format, const gchar *key); gboolean gst_amc_format_get_float (GstAmcFormat *format, const gchar *key, gfloat *value); -void gst_amc_format_set_float (GstAmcFormat *format, const gchar *key, gfloat *value); +void gst_amc_format_set_float (GstAmcFormat *format, const gchar *key, gfloat value); gboolean gst_amc_format_get_int (GstAmcFormat *format, const gchar *key, gint *value); -void gst_amc_format_set_int (GstAmcFormat *format, const gchar *key, gint *value); +void gst_amc_format_set_int (GstAmcFormat *format, const gchar *key, gint value); gboolean gst_amc_format_get_string (GstAmcFormat *format, const gchar *key, gchar **value); void gst_amc_format_set_string (GstAmcFormat *format, const gchar *key, const gchar *value); +GstVideoFormat gst_amc_color_format_to_video_format (gint color_format); +gint gst_amc_video_format_to_color_format (GstVideoFormat video_format); + +const gchar * gst_amc_avc_profile_to_string (gint profile); +gint gst_amc_avc_profile_from_string (const gchar *profile); +const gchar * gst_amc_avc_level_to_string (gint level); +gint gst_amc_avc_level_from_string (const gchar *level); +gint gst_amc_h263_profile_to_gst_id (gint profile); +gint gst_amc_h263_profile_from_gst_id (gint profile); +gint gst_amc_h263_level_to_gst_id (gint level); +gint gst_amc_h263_level_from_gst_id (gint level); +const gchar * gst_amc_mpeg4_profile_to_string (gint profile); +gint gst_amc_avc_mpeg4_profile_from_string (const gchar *profile); +const gchar * gst_amc_mpeg4_level_to_string (gint level); +gint gst_amc_mpeg4_level_from_string (const gchar *level); +const gchar * gst_amc_aac_profile_to_string (gint profile); +gint gst_amc_aac_profile_from_string (const gchar *profile); + G_END_DECLS #endif /* __GST_AMC_H__ */