diff --git a/sys/androidmedia/gstamc.c b/sys/androidmedia/gstamc.c index 9eb61ab744..5c8be9097f 100644 --- a/sys/androidmedia/gstamc.c +++ b/sys/androidmedia/gstamc.c @@ -37,7 +37,7 @@ 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; -JNIEnv * +static JNIEnv * gst_amc_attach_current_thread (void) { JNIEnv *env; @@ -47,7 +47,7 @@ gst_amc_attach_current_thread (void) args.name = NULL; args.group = NULL; - if ((*java_vm)->AttachCurrentThread (java_vm, (void **) &env, &args) < 0) { + if ((*java_vm)->AttachCurrentThread (java_vm, &env, &args) < 0) { GST_ERROR ("Failed to attach current thread"); return NULL; } @@ -55,7 +55,7 @@ gst_amc_attach_current_thread (void) return env; } -void +static void gst_amc_detach_current_thread (void) { (*java_vm)->DetachCurrentThread (java_vm); @@ -131,6 +131,526 @@ create_failed: } } + +GstAmcCodec * +gst_amc_codec_new (const gchar * name) +{ + return NULL; +} + +void +gst_amc_codec_free (GstAmcCodec * codec) +{ + +} + +void +gst_amc_codec_configure (GstAmcCodec * codec, gint flags) +{ + +} + +GstAmcFormat * +gst_amc_codec_get_output_format (GstAmcCodec * codec) +{ + return NULL; +} + +void +gst_amc_codec_start (GstAmcCodec * codec) +{ + +} + +void +gst_amc_codec_stop (GstAmcCodec * codec) +{ + +} + +void +gst_amc_codec_flush (GstAmcCodec * codec) +{ + +} + +GstAmcBuffer * +gst_amc_codec_get_output_buffers (GstAmcCodec * codec, gsize * n_buffers) +{ + return NULL; +} + +GstAmcBuffer * +gst_amc_codec_get_input_buffers (GstAmcCodec * codec, gsize * n_buffers) +{ + return NULL; +} + +gint +gst_amc_codec_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs) +{ + return -1; +} + +gint +gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec, + GstAmcBufferInfo * info, gint64 timeoutUs) +{ + return -1; +} + +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) +{ + +} + +GstAmcFormat * +gst_amc_format_new_audio (const gchar * mime, gint sample_rate, gint channels) +{ + return NULL; +} + +GstAmcFormat * +gst_amc_format_new_video (const gchar * mime, gint width, gint height) +{ + return NULL; +} + +void +gst_amc_format_free (GstAmcFormat * format) +{ + +} + +gboolean +gst_amc_format_contains_key (GstAmcFormat * format, const gchar * key) +{ + return FALSE; +} + +gboolean +gst_amc_format_get_float (GstAmcFormat * format, const gchar * key, + gfloat * value) +{ + return FALSE; +} + +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) +{ + return FALSE; +} + +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) +{ + return FALSE; +} + +void +gst_amc_format_set_string (GstAmcFormat * format, const gchar * key, + const gchar * value) +{ + +} + +static GList *codec_infos = NULL; + +static gboolean +register_codecs (void) +{ + gboolean ret = TRUE; + JNIEnv *env; + jclass codec_list_class = NULL; + jmethodID get_codec_count_id, get_codec_info_at_id; + jint codec_count, i; + + env = gst_amc_attach_current_thread (); + + codec_list_class = (*env)->FindClass (env, "android/media/MediaCodecList"); + if (!codec_list_class) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get codec list class"); + goto done; + } + + get_codec_count_id = + (*env)->GetStaticMethodID (env, codec_list_class, "getCodecCount", "()I"); + get_codec_info_at_id = + (*env)->GetStaticMethodID (env, codec_list_class, "getCodecInfoAt", + "(I)Landroid/media/MediaCodecInfo;"); + if (!get_codec_count_id || !get_codec_info_at_id) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get codec list method IDs"); + goto done; + } + + codec_count = + (*env)->CallStaticIntMethod (env, codec_list_class, get_codec_count_id); + if ((*env)->ExceptionCheck (env)) { + ret = FALSE; + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get number of available codecs"); + goto done; + } + + GST_LOG ("Found %d available codecs", codec_count); + + for (i = 0; i < codec_count; i++) { + GstAmcCodecInfo *gst_codec_info; + jobject codec_info = NULL; + jclass codec_info_class = NULL; + jmethodID get_capabilities_for_type_id, get_name_id; + jmethodID get_supported_types_id, is_encoder_id; + jobject name = NULL; + const gchar *name_str = NULL; + jboolean is_encoder; + jarray supported_types = NULL; + jsize n_supported_types; + jsize j; + gboolean valid_type = FALSE; + + gst_codec_info = g_new0 (GstAmcCodecInfo, 1); + + codec_info = + (*env)->CallStaticObjectMethod (env, codec_list_class, + get_codec_info_at_id, i); + if ((*env)->ExceptionCheck (env) || !codec_info) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get codec info %d", i); + goto next_codec; + } + + codec_info_class = (*env)->GetObjectClass (env, codec_info); + if (!codec_list_class) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get codec info class"); + goto next_codec; + } + + get_capabilities_for_type_id = + (*env)->GetMethodID (env, codec_info_class, "getCapabilitiesForType", + "(Ljava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;"); + get_name_id = + (*env)->GetMethodID (env, codec_info_class, "getName", + "()Ljava/lang/String;"); + get_supported_types_id = + (*env)->GetMethodID (env, codec_info_class, "getSupportedTypes", + "()[Ljava/lang/String;"); + is_encoder_id = + (*env)->GetMethodID (env, codec_info_class, "isEncoder", "()Z"); + if (!get_capabilities_for_type_id || !get_name_id + || !get_supported_types_id || !is_encoder_id) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get codec info method IDs"); + goto next_codec; + } + + name = (*env)->CallObjectMethod (env, codec_info, get_name_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get codec name"); + 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"); + goto next_codec; + } + + GST_INFO ("Checking codec '%s'", name_str); + + /* Compatibility codec names */ + if (strcmp (name_str, "AACEncoder") == 0 || + strcmp (name_str, "OMX.google.raw.decoder") == 0) { + GST_INFO ("Skipping compatibility codec '%s'", name_str); + goto next_codec; + } + gst_codec_info->name = g_strdup (name_str); + + is_encoder = (*env)->CallBooleanMethod (env, codec_info, is_encoder_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to detect if codec is an encoder"); + goto next_codec; + } + gst_codec_info->is_encoder = is_encoder; + + supported_types = + (*env)->CallObjectMethod (env, codec_info, get_supported_types_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get supported types"); + goto next_codec; + } + + n_supported_types = (*env)->GetArrayLength (env, supported_types); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get supported types array length"); + goto next_codec; + } + + GST_INFO ("Codec '%s' has %d supported types", name_str, n_supported_types); + + gst_codec_info->supported_types = g_new0 (GstAmcCodecType, 1); + gst_codec_info->n_supported_types = n_supported_types; + + for (j = 0; j < n_supported_types; j++) { + GstAmcCodecType *gst_codec_type; + jobject supported_type = NULL; + const gchar *supported_type_str = NULL; + jobject capabilities = NULL; + jclass capabilities_class = NULL; + jfieldID color_formats_id, profile_levels_id; + jobject color_formats = NULL; + jobject profile_levels = NULL; + jint *color_formats_elems = NULL; + jsize n_elems, k; + + gst_codec_type = &gst_codec_info->supported_types[j]; + + supported_type = (*env)->GetObjectArrayElement (env, supported_types, j); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get %d-th supported type", j); + goto next_supported_type; + } + + supported_type_str = + (*env)->GetStringUTFChars (env, supported_type, NULL); + if ((*env)->ExceptionCheck (env) || !supported_type_str) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to convert supported type to UTF8"); + 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, + get_capabilities_for_type_id, supported_type); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get capabilities for supported type"); + goto next_supported_type; + } + + capabilities_class = (*env)->GetObjectClass (env, capabilities); + if (!capabilities_class) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get capabilities class"); + goto next_supported_type; + } + + color_formats_id = + (*env)->GetFieldID (env, capabilities_class, "colorFormats", "[I"); + profile_levels_id = + (*env)->GetFieldID (env, capabilities_class, "profileLevels", + "[Landroid/media/MediaCodecInfo$CodecProfileLevel;"); + if (!color_formats_id || !profile_levels_id) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get capabilities field IDs"); + goto next_supported_type; + } + + color_formats = + (*env)->GetObjectField (env, capabilities, color_formats_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get color formats"); + goto next_supported_type; + } + + n_elems = (*env)->GetArrayLength (env, color_formats); + gst_codec_type->n_color_formats = n_elems; + gst_codec_type->color_formats = g_new0 (gint, n_elems); + color_formats_elems = + (*env)->GetIntArrayElements (env, color_formats, NULL); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get color format elements"); + goto next_supported_type; + } + + for (k = 0; k < n_elems; k++) { + GST_INFO ("Color format %d: %d", k, color_formats_elems[k]); + gst_codec_type->color_formats[k] = color_formats_elems[k]; + } + + profile_levels = + (*env)->GetObjectField (env, capabilities, profile_levels_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get profile/levels"); + goto next_supported_type; + } + + n_elems = (*env)->GetArrayLength (env, profile_levels); + gst_codec_type->n_profile_levels = n_elems; + gst_codec_type->profile_levels = + g_malloc0 (sizeof (gst_codec_type->profile_levels) * n_elems); + for (k = 0; k < n_elems; k++) { + jobject profile_level = NULL; + jclass profile_level_class = NULL; + jfieldID level_id, profile_id; + jint level, profile; + + profile_level = (*env)->GetObjectArrayElement (env, profile_levels, k); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get %d-th profile/level", k); + goto next_profile_level; + } + + profile_level_class = (*env)->GetObjectClass (env, profile_level); + if (!profile_level_class) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get profile/level class"); + goto next_profile_level; + } + + level_id = (*env)->GetFieldID (env, profile_level_class, "level", "I"); + profile_id = + (*env)->GetFieldID (env, profile_level_class, "profile", "I"); + if (!level_id || !profile_id) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get profile/level field IDs"); + goto next_profile_level; + } + + level = (*env)->GetIntField (env, profile_level, level_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get level"); + goto next_profile_level; + } + GST_INFO ("Level %d: 0x%08x", k, level); + gst_codec_type->profile_levels[k].level = level; + + profile = (*env)->GetIntField (env, profile_level, profile_id); + if ((*env)->ExceptionCheck (env)) { + (*env)->ExceptionClear (env); + GST_ERROR ("Failed to get profile"); + goto next_profile_level; + } + GST_INFO ("Profile %d: 0x%08x", k, profile); + gst_codec_type->profile_levels[k].profile = profile; + + next_profile_level: + if (profile_level) + (*env)->DeleteLocalRef (env, profile_level); + profile_level = NULL; + if (profile_level_class) + (*env)->DeleteLocalRef (env, profile_level_class); + profile_level_class = NULL; + } + + next_supported_type: + if (color_formats_elems) + (*env)->ReleaseIntArrayElements (env, color_formats, + color_formats_elems, JNI_ABORT); + color_formats_elems = NULL; + if (color_formats) + (*env)->DeleteLocalRef (env, color_formats); + color_formats = NULL; + if (profile_levels) + (*env)->DeleteLocalRef (env, profile_levels); + color_formats = NULL; + if (capabilities) + (*env)->DeleteLocalRef (env, capabilities); + capabilities = NULL; + if (capabilities_class) + (*env)->DeleteLocalRef (env, capabilities_class); + capabilities_class = NULL; + if (supported_type_str) + (*env)->ReleaseStringUTFChars (env, supported_type, supported_type_str); + supported_type_str = NULL; + if (supported_type) + (*env)->DeleteLocalRef (env, supported_type); + supported_type = NULL; + } + + /* We need at least a valid supported type */ + if (valid_type) { + GST_LOG ("Successfully scanned codec '%s'", name_str); + codec_infos = g_list_append (codec_infos, gst_codec_info); + gst_codec_info = NULL; + } + + /* Clean up of all local references we got */ + next_codec: + if (name_str) + (*env)->ReleaseStringUTFChars (env, name, name_str); + name_str = NULL; + if (name) + (*env)->DeleteLocalRef (env, name); + name = NULL; + if (supported_types) + (*env)->DeleteLocalRef (env, supported_types); + supported_types = NULL; + if (codec_info) + (*env)->DeleteLocalRef (env, codec_info); + codec_info = NULL; + if (codec_info_class) + (*env)->DeleteLocalRef (env, codec_info_class); + codec_info_class = NULL; + if (gst_codec_info) { + gint j; + + for (j = 0; j < gst_codec_info->n_supported_types; j++) { + GstAmcCodecType *gst_codec_type = &gst_codec_info->supported_types[j]; + + g_free (gst_codec_type->mime); + g_free (gst_codec_type->color_formats); + g_free (gst_codec_type->profile_levels); + } + g_free (gst_codec_info->supported_types); + g_free (gst_codec_info->name); + g_free (gst_codec_info); + } + gst_codec_info = NULL; + valid_type = FALSE; + } + + ret = codec_infos != NULL; + +done: + if (codec_list_class) + (*env)->DeleteLocalRef (env, codec_list_class); + + gst_amc_detach_current_thread (); + + return ret; +} + static gboolean plugin_init (GstPlugin * plugin) { @@ -139,6 +659,9 @@ plugin_init (GstPlugin * plugin) if (!initialize_java_vm ()) return FALSE; + if (!register_codecs ()) + return FALSE; + return TRUE; } diff --git a/sys/androidmedia/gstamc.h b/sys/androidmedia/gstamc.h index e452e1b67f..7c6398f25f 100644 --- a/sys/androidmedia/gstamc.h +++ b/sys/androidmedia/gstamc.h @@ -26,8 +26,91 @@ G_BEGIN_DECLS -JNIEnv * gst_amc_attach_current_thread (void); -void gst_amc_detach_current_thread (void); +typedef struct _GstAmcCodecInfo GstAmcCodecInfo; +typedef struct _GstAmcCodecType GstAmcCodecType; +typedef struct _GstAmcCodec GstAmcCodec; +typedef struct _GstAmcCodecBufferInfo GstAmcBufferInfo; +typedef struct _GstAmcFormat GstAmcFormat; +typedef struct _GstAmcBuffer GstAmcBuffer; + +struct _GstAmcCodecType { + gchar *mime; + + gint *color_formats; + gint n_color_formats; + + struct { + gint profile; + gint level; + } *profile_levels; + gint n_profile_levels; +}; + +struct _GstAmcCodecInfo { + gchar *name; + gboolean is_encoder; + GstAmcCodecType *supported_types; + gint n_supported_types; +}; + +struct _GstAmcBuffer { + guint8 *data; + gsize len; +}; + +struct _GstAmcFormat { + /* < private > */ + jobject format; /* global reference */ + jclass format_class; /* global reference */ +}; + +struct _GstAmcCodec { + /* < private > */ + jobject codec; /* global reference */ + jclass codec_class; /* global reference */ +}; + +struct _GstAmcBufferInfo { + gint flags; + gint offset; + gint64 presentationTimeUs; + 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); +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); + +GstAmcBuffer * gst_amc_codec_get_output_buffers (GstAmcCodec * codec, gsize * n_buffers); +GstAmcBuffer * gst_amc_codec_get_input_buffers (GstAmcCodec * codec, 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); + + +GstAmcFormat * gst_amc_format_new_audio (const gchar *mime, gint sample_rate, gint channels); +GstAmcFormat * gst_amc_format_new_video (const gchar *mime, gint width, gint height); +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); +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); +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); G_END_DECLS