diff --git a/subprojects/gst-plugins-bad/sys/androidmedia/jni/gstamc-jni.c b/subprojects/gst-plugins-bad/sys/androidmedia/jni/gstamc-jni.c index e20abcce7f..790408cfb1 100644 --- a/subprojects/gst-plugins-bad/sys/androidmedia/jni/gstamc-jni.c +++ b/subprojects/gst-plugins-bad/sys/androidmedia/jni/gstamc-jni.c @@ -17,23 +17,84 @@ * */ +#include + #include "../gstamc.h" #include "gstamc-jni.h" +#define GST_CAT_DEFAULT gst_amc_debug + +#ifdef HAVE_NDKMEDIA + +#include "../ndk/gstamc-ndk.h" + +static gboolean +should_prefer_ndk () +{ + int device_api_level = android_get_device_api_level (); + const gchar *impl_env = g_getenv ("GST_AMC_PREFERED_IMPL"); + + if (device_api_level < 21) { + /* NDK media APIs are added in API level 21 (Android 5). Don't bother + * trying. It'll fail anyway. */ + return FALSE; + } + + if (impl_env) { + if (g_str_equal (impl_env, "ndk")) + return TRUE; + else if (g_str_equal (impl_env, "jni")) + return FALSE; + else + GST_WARNING ("Unknown value '%s' for GST_AMC_PREFERED_IMPL, ignored.", + impl_env); + } + + /* NDK's AMediaCodec_setParameters() is available since API level 26, while + * Java's MediaCodec.setParameters() is available since API level 19. Prefers + * JNI version for 21 <= API level < 26 to avoid feature regression. + */ + return (device_api_level >= 26); +} +#endif /* HAVE_NDKMEDIA */ + gboolean gst_amc_static_init (void) { if (!gst_amc_codeclist_jni_static_init ()) return FALSE; + if (!gst_amc_surface_texture_jni_static_init ()) + return FALSE; + +#ifdef HAVE_NDKMEDIA + + if (!should_prefer_ndk ()) + goto init_jni; + + if (!gst_amc_codec_ndk_static_init ()) + goto init_jni; + + if (!gst_amc_format_ndk_static_init ()) + goto init_jni; + + GST_INFO ("Use NDK implementation for GstAmc."); + + gst_amc_format_vtable = &gst_amc_format_ndk_vtable; + gst_amc_codec_vtable = &gst_amc_codec_ndk_vtable; + + return TRUE; + +#endif /* HAVE_NDKMEDIA */ + +init_jni: if (!gst_amc_codec_jni_static_init ()) return FALSE; if (!gst_amc_format_jni_static_init ()) return FALSE; - if (!gst_amc_surface_texture_jni_static_init ()) - return FALSE; + GST_INFO ("Use JNI implementation for GstAmc."); gst_amc_format_vtable = &gst_amc_format_jni_vtable; gst_amc_codec_vtable = &gst_amc_codec_jni_vtable; diff --git a/subprojects/gst-plugins-bad/sys/androidmedia/meson.build b/subprojects/gst-plugins-bad/sys/androidmedia/meson.build index f5a94b189f..bb0067a54f 100644 --- a/subprojects/gst-plugins-bad/sys/androidmedia/meson.build +++ b/subprojects/gst-plugins-bad/sys/androidmedia/meson.build @@ -75,6 +75,19 @@ else 'jni/gstamcsurface.c', 'jni/gstamcsurfacetexture-jni.c', ] + + # Build an Ndk support if its headers exists. We don't actually link into it + # but use only type definations (and then rely on dlopen and dlsym), so this + # doesn't affect compatibility with older Android versions. + if cc.check_header('media/NdkMediaCodec.h') + androidmedia_sources += [ + 'ndk/gstamc-codec-ndk.c', + 'ndk/gstamc-format-ndk.c', + ] + extra_cargs += [ '-DHAVE_NDKMEDIA' ] + extra_deps += [ cc.find_library('android', required : true) ] + endif + plugin_name = 'gstandroidmedia' endif diff --git a/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-codec-ndk.c b/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-codec-ndk.c new file mode 100644 index 0000000000..98b2f26e73 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-codec-ndk.c @@ -0,0 +1,584 @@ +/* + * Copyright (C) 2012,2018 Collabora Ltd. + * Author: Sebastian Dröge + * Copyright (C) 2015, Sebastian Dröge + * Copyright (C) 2023, Ratchanan Srirattanamet + * + * 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 "gstamc-ndk.h" +#include "gstamc-internal-ndk.h" +#include "../gstamc-codec.h" +#include "../gstamc-constants.h" + +#include "gstjniutils.h" +#include "../jni/gstamcsurface.h" + +#include +#include +#include +#include + +#include + +struct _GstAmcCodec +{ + AMediaCodec *ndk_media_codec; + gboolean is_encoder; + + /* For JNI-based SurfaceTexture. */ + GstAmcSurface *surface; +}; + +/* The defines are from NdkMediaCodec.h. See the reasoning in the same file. */ +#if defined(__USE_FILE_OFFSET64) && !defined(__LP64__) +#define _off_t_compat int32_t +#else +#define _off_t_compat off_t +#endif /* defined(__USE_FILE_OFFSET64) && !defined(__LP64__) */ + +static struct +{ + void *mediandk_handle; + + AMediaCodec *(*create_codec_by_name) (const char *name); + media_status_t (*delete) (AMediaCodec *); + + media_status_t (*configure) (AMediaCodec *, + const AMediaFormat * format, + ANativeWindow * surface, AMediaCrypto * crypto, uint32_t flags); + + media_status_t (*start) (AMediaCodec *); + media_status_t (*stop) (AMediaCodec *); + media_status_t (*flush) (AMediaCodec *); + + uint8_t *(*get_input_buffer) (AMediaCodec *, size_t idx, size_t *out_size); + uint8_t *(*get_output_buffer) (AMediaCodec *, size_t idx, size_t *out_size); + ssize_t (*dequeue_input_buffer) (AMediaCodec *, int64_t timeoutUs); + + media_status_t (*queue_input_buffer) (AMediaCodec *, + size_t idx, + _off_t_compat offset, size_t size, uint64_t time, uint32_t flags); + + ssize_t (*dequeue_output_buffer) (AMediaCodec *, + AMediaCodecBufferInfo * info, int64_t timeoutUs); + + AMediaFormat *(*get_output_format) (AMediaCodec *); + + media_status_t (*release_output_buffer) (AMediaCodec *, + size_t idx, bool render); + + /* optional */ + media_status_t (*set_parameters) (AMediaCodec * mData, + const AMediaFormat * params); +} a_media_codec; + +#undef _off_t_compat + +gboolean +gst_amc_codec_ndk_static_init (void) +{ + a_media_codec.mediandk_handle = dlopen ("libmediandk.so", RTLD_NOW); + + if (!a_media_codec.mediandk_handle) + return FALSE; + + a_media_codec.create_codec_by_name = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_createCodecByName"); + a_media_codec.delete = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_delete"); + a_media_codec.configure = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_configure"); + a_media_codec.start = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_start"); + a_media_codec.stop = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_stop"); + a_media_codec.flush = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_flush"); + a_media_codec.get_input_buffer = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_getInputBuffer"); + a_media_codec.get_output_buffer = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_getOutputBuffer"); + a_media_codec.dequeue_input_buffer = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_dequeueInputBuffer"); + a_media_codec.queue_input_buffer = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_queueInputBuffer"); + a_media_codec.dequeue_output_buffer = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_dequeueOutputBuffer"); + a_media_codec.get_output_format = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_getOutputFormat"); + a_media_codec.release_output_buffer = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_releaseOutputBuffer"); + + if (!a_media_codec.create_codec_by_name || !a_media_codec.delete + || !a_media_codec.configure || !a_media_codec.start || !a_media_codec.stop + || !a_media_codec.flush || !a_media_codec.get_input_buffer + || !a_media_codec.get_output_buffer || !a_media_codec.dequeue_input_buffer + || !a_media_codec.queue_input_buffer + || !a_media_codec.dequeue_output_buffer + || !a_media_codec.get_output_format + || !a_media_codec.release_output_buffer) { + GST_WARNING ("Failed to get AMediaCodec functions"); + dlclose (a_media_codec.mediandk_handle); + return FALSE; + } + + /* Optional. */ + a_media_codec.set_parameters = + dlsym (a_media_codec.mediandk_handle, "AMediaCodec_setParameters"); + + return TRUE; +} + +static void +gst_amc_buffer_ndk_free (GstAmcBuffer * buffer) +{ + g_free (buffer); +} + +static gboolean +gst_amc_buffer_ndk_set_position_and_limit (GstAmcBuffer * buffer_, + GError ** err, gint position, gint limit) +{ +/* FIXME: Do we need to do something? + buffer->data = buffer->data + position; + buffer->size = limit; +*/ + return TRUE; +} + +static GstAmcCodec * +gst_amc_codec_ndk_new (const gchar * name, gboolean is_encoder, GError ** err) +{ + GstAmcCodec *codec = NULL; + + g_return_val_if_fail (name != NULL, NULL); + + codec = g_new0 (GstAmcCodec, 1); + codec->ndk_media_codec = a_media_codec.create_codec_by_name (name); + codec->is_encoder = is_encoder; + + if (!codec->ndk_media_codec) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to create codec by name %s", name); + g_free (codec); + return NULL; + } + + return codec; +} + +static void +gst_amc_codec_ndk_free (GstAmcCodec * codec) +{ + media_status_t result; + + result = a_media_codec.delete (codec->ndk_media_codec); + + if (result != AMEDIA_OK) { + GST_WARNING + ("Unable to delete an AMediaCodec: %d, a leak might have occured.", + result); + } + + if (codec->surface) + g_object_unref (codec->surface); + + g_free (codec); +} + +static gboolean +gst_amc_codec_ndk_configure (GstAmcCodec * codec, GstAmcFormat * format, + GstAmcSurfaceTexture * surface_texture, GError ** err) +{ + gboolean ret; + media_status_t result; + ANativeWindow *native_window = NULL; + uint32_t flags = 0; + + g_return_val_if_fail (codec != NULL, FALSE); + g_return_val_if_fail (format != NULL, FALSE); + g_return_val_if_fail (surface_texture == NULL + || GST_IS_AMC_SURFACE_TEXTURE_JNI (surface_texture), FALSE); + + if (surface_texture) { + if (codec->surface) + g_object_unref (codec->surface); + + if (GST_IS_AMC_SURFACE_TEXTURE_JNI (surface_texture)) { + JNIEnv *env; + + codec->surface = gst_amc_surface_new ( + (GstAmcSurfaceTextureJNI *) surface_texture, err); + if (!codec->surface) + return FALSE; + + env = gst_amc_jni_get_env (); + native_window = ANativeWindow_fromSurface (env, codec->surface->jobject); + + if (!native_window) + return FALSE; + /* TODO: support NDK-based ASurfaceTexture. */ + } else { + g_assert_not_reached (); + } + } + + if (codec->is_encoder) + flags = AMEDIACODEC_CONFIGURE_FLAG_ENCODE; + + result = a_media_codec.configure (codec->ndk_media_codec, + format->ndk_media_format, native_window, NULL, flags); + + if (result != AMEDIA_OK) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to configure codec: %d", result); + ret = FALSE; + goto out; + } + + ret = TRUE; + +out: + if (native_window) + ANativeWindow_release (native_window); + + return ret; +} + +static GstAmcFormat * +gst_amc_codec_ndk_get_output_format (GstAmcCodec * codec, GError ** err) +{ + AMediaFormat *ndk_media_format; + + g_return_val_if_fail (codec != NULL, NULL); + + ndk_media_format = a_media_codec.get_output_format (codec->ndk_media_codec); + if (!ndk_media_format) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to get output format"); + return NULL; + } + + return gst_amc_format_ndk_from_a_media_format (ndk_media_format); +} + +static gboolean +gst_amc_codec_ndk_start (GstAmcCodec * codec, GError ** err) +{ + media_status_t result; + + g_return_val_if_fail (codec != NULL, FALSE); + + result = a_media_codec.start (codec->ndk_media_codec); + + if (result != AMEDIA_OK) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to start codec: %d", result); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_amc_codec_ndk_stop (GstAmcCodec * codec, GError ** err) +{ + media_status_t result; + + g_return_val_if_fail (codec != NULL, FALSE); + + result = a_media_codec.stop (codec->ndk_media_codec); + + if (result != AMEDIA_OK) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to stop codec: %d", result); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_amc_codec_ndk_flush (GstAmcCodec * codec, GError ** err) +{ + media_status_t result; + + g_return_val_if_fail (codec != NULL, FALSE); + + result = a_media_codec.flush (codec->ndk_media_codec); + + if (result != AMEDIA_OK) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to flush codec: %d", result); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_amc_codec_ndk_set_parameter (GstAmcCodec * codec, GError ** err, + const gchar * key, int value) +{ + GstAmcFormat *format; + media_status_t result; + gboolean ret = TRUE; + + if (!a_media_codec.set_parameters) { + /* Not available means we're on Android < 26 + * Note: this is a degradation in feature compared to JNI counterpart. */ + return FALSE; + } + + format = gst_amc_format_ndk_new (err); + if (!format) + return FALSE; + + if (!gst_amc_format_set_int (format, key, value, err)) { + ret = FALSE; + goto done; + } + + result = + a_media_codec.set_parameters (codec->ndk_media_codec, + format->ndk_media_format); + + if (result != AMEDIA_OK) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to set a parameter: %d", result); + ret = FALSE; + } + +done: + gst_amc_format_free (format); + + return ret; +} + +#define PARAMETER_KEY_REQUEST_SYNC_FRAME "request-sync" + +static gboolean +gst_amc_codec_ndk_request_key_frame (GstAmcCodec * codec, GError ** err) +{ + g_return_val_if_fail (codec != NULL, FALSE); + + return gst_amc_codec_ndk_set_parameter (codec, err, + PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); +} + +static gboolean +gst_amc_codec_ndk_have_dynamic_bitrate () +{ + /* Dynamic bitrate scaling is supported on Android >= 26, + * where the setParameters() call is available + * Note: this is a degradation in feature compared to JNI counterpart. */ + return (a_media_codec.set_parameters != NULL); +} + +#define PARAMETER_KEY_VIDEO_BITRATE "video-bitrate" + +static gboolean +gst_amc_codec_ndk_set_dynamic_bitrate (GstAmcCodec * codec, GError ** err, + gint bitrate) +{ + g_return_val_if_fail (codec != NULL, FALSE); + + return gst_amc_codec_ndk_set_parameter (codec, err, + PARAMETER_KEY_VIDEO_BITRATE, bitrate); +} + +static gboolean +gst_amc_codec_ndk_release (GstAmcCodec * codec, GError ** err) +{ + g_return_val_if_fail (codec != NULL, FALSE); + + /* Do nothing. AMediaCodec_delete() already covers this. */ + return TRUE; +} + +static GstAmcBuffer * +gst_amc_codec_ndk_get_output_buffer (GstAmcCodec * codec, gint index, + GError ** err) +{ + GstAmcBuffer *ret; + + g_return_val_if_fail (codec != NULL, NULL); + + ret = g_new0 (GstAmcBuffer, 1); + ret->data = a_media_codec.get_output_buffer (codec->ndk_media_codec, + index, &ret->size); + + if (!ret->data) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to get output buffer for idx %d.", index); + g_free (ret); + return NULL; + } + + return ret; +} + +static GstAmcBuffer * +gst_amc_codec_ndk_get_input_buffer (GstAmcCodec * codec, gint index, + GError ** err) +{ + GstAmcBuffer *ret; + + g_return_val_if_fail (codec != NULL, NULL); + + ret = g_new0 (GstAmcBuffer, 1); + ret->data = a_media_codec.get_input_buffer (codec->ndk_media_codec, + index, &ret->size); + + if (!ret->data) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to get input buffer for idx %d.", index); + g_free (ret); + return NULL; + } + + return ret; +} + +static gint +gst_amc_codec_ndk_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs, + GError ** err) +{ + gint ret; + + g_return_val_if_fail (codec != NULL, G_MININT); + + ret = a_media_codec.dequeue_input_buffer (codec->ndk_media_codec, timeoutUs); + + /* AMediaCodec's error code is the same as Java's MediaCodec, thus no + * translation is required. */ + + return ret; +} + +static gint +gst_amc_codec_ndk_dequeue_output_buffer (GstAmcCodec * codec, + GstAmcBufferInfo * info, gint64 timeoutUs, GError ** err) +{ + gint ret; + AMediaCodecBufferInfo a_info; + + g_return_val_if_fail (codec != NULL, G_MININT); + + ret = a_media_codec.dequeue_output_buffer (codec->ndk_media_codec, + &a_info, timeoutUs); + + if (ret == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) { + return gst_amc_codec_ndk_dequeue_output_buffer (codec, info, timeoutUs, + err); + } else if (ret < 0) { + /* AMediaCodec's error code is the same as Java's MediaCodec, thus no + * translation is required. */ + return ret; + } + + info->flags = a_info.flags; + info->offset = a_info.offset; + info->presentation_time_us = a_info.presentationTimeUs; + info->size = a_info.size; + + return ret; +} + +static gboolean +gst_amc_codec_ndk_queue_input_buffer (GstAmcCodec * codec, gint index, + const GstAmcBufferInfo * info, GError ** err) +{ + media_status_t result; + + g_return_val_if_fail (codec != NULL, FALSE); + + result = a_media_codec.queue_input_buffer (codec->ndk_media_codec, + index, info->offset, info->size, info->presentation_time_us, info->flags); + + if (result != AMEDIA_OK) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to queue input buffer: %d", result); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_amc_codec_ndk_release_output_buffer (GstAmcCodec * codec, gint index, + gboolean render, GError ** err) +{ + media_status_t result; + + g_return_val_if_fail (codec != NULL, FALSE); + + result = a_media_codec.release_output_buffer (codec->ndk_media_codec, + index, render); + + if (result != AMEDIA_OK) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to release input buffer: %d", result); + return FALSE; + } + + return TRUE; +} + +static GstAmcSurfaceTexture * +gst_amc_codec_ndk_new_surface_texture (GError ** err) +{ + /* TODO: support NDK-based ASurfaceTexture. */ + return (GstAmcSurfaceTexture *) gst_amc_surface_texture_jni_new (err); +} + +GstAmcCodecVTable gst_amc_codec_ndk_vtable = { + .buffer_free = gst_amc_buffer_ndk_free, + .buffer_set_position_and_limit = gst_amc_buffer_ndk_set_position_and_limit, + + .create = gst_amc_codec_ndk_new, + .free = gst_amc_codec_ndk_free, + + .configure = gst_amc_codec_ndk_configure, + .get_output_format = gst_amc_codec_ndk_get_output_format, + + .start = gst_amc_codec_ndk_start, + .stop = gst_amc_codec_ndk_stop, + .flush = gst_amc_codec_ndk_flush, + .request_key_frame = gst_amc_codec_ndk_request_key_frame, + + .have_dynamic_bitrate = gst_amc_codec_ndk_have_dynamic_bitrate, + .set_dynamic_bitrate = gst_amc_codec_ndk_set_dynamic_bitrate, + + .release = gst_amc_codec_ndk_release, + + .get_output_buffer = gst_amc_codec_ndk_get_output_buffer, + .get_input_buffer = gst_amc_codec_ndk_get_input_buffer, + + .dequeue_input_buffer = gst_amc_codec_ndk_dequeue_input_buffer, + .dequeue_output_buffer = gst_amc_codec_ndk_dequeue_output_buffer, + + .queue_input_buffer = gst_amc_codec_ndk_queue_input_buffer, + .release_output_buffer = gst_amc_codec_ndk_release_output_buffer, + + .new_surface_texture = gst_amc_codec_ndk_new_surface_texture, +}; diff --git a/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-format-ndk.c b/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-format-ndk.c new file mode 100644 index 0000000000..2d2eed606c --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-format-ndk.c @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2012,2018 Collabora Ltd. + * Author: Sebastian Dröge + * Copyright (C) 2015, Sebastian Dröge + * Copyright (C) 2023, Ratchanan Srirattanamet + * + * 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 "../gstamc-format.h" +#include "gstamc-ndk.h" +#include "gstamc-internal-ndk.h" + +#include + +#include +#include + +static struct +{ + void *mediandk_handle; + + AMediaFormat *(*new) (); + media_status_t (*delete) (AMediaFormat *); + + const char *(*to_string) (AMediaFormat *); + + bool (*get_int32) (AMediaFormat *, const char *name, int32_t * out); + bool (*get_float) (AMediaFormat *, const char *name, float *out); + bool (*get_buffer) (AMediaFormat *, const char *name, void **data, + size_t *size); + bool (*get_string) (AMediaFormat *, const char *name, const char **out); + + void (*set_int32) (AMediaFormat *, const char *name, int32_t value); + void (*set_float) (AMediaFormat *, const char *name, float value); + void (*set_string) (AMediaFormat *, const char *name, const char *value); + void (*set_buffer) (AMediaFormat *, const char *name, const void *data, + size_t size); +} a_media_format; + +gboolean +gst_amc_format_ndk_static_init (void) +{ + a_media_format.mediandk_handle = dlopen ("libmediandk.so", RTLD_NOW); + + if (!a_media_format.mediandk_handle) + return FALSE; + + a_media_format.new = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_new"); + a_media_format.delete = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_delete"); + a_media_format.to_string = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_toString"); + a_media_format.get_int32 = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_getInt32"); + a_media_format.get_float = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_getFloat"); + a_media_format.get_buffer = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_getBuffer"); + a_media_format.get_string = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_getString"); + a_media_format.set_int32 = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_setInt32"); + a_media_format.set_float = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_setFloat"); + a_media_format.set_string = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_setString"); + a_media_format.set_buffer = + dlsym (a_media_format.mediandk_handle, "AMediaFormat_setBuffer"); + + if (!a_media_format.new || !a_media_format.delete || !a_media_format.to_string + || !a_media_format.get_int32 || !a_media_format.get_float + || !a_media_format.get_buffer || !a_media_format.get_string + || !a_media_format.set_int32 || !a_media_format.set_float + || !a_media_format.set_string || !a_media_format.set_buffer) { + GST_WARNING ("Failed to get AMediaFormat functions"); + dlclose (a_media_format.mediandk_handle); + a_media_format.mediandk_handle = NULL; + return FALSE; + } + + return TRUE; +} + +GstAmcFormat * +gst_amc_format_ndk_new (GError ** err) +{ + GstAmcFormat *ret = g_new0 (GstAmcFormat, 1); + + ret->ndk_media_format = a_media_format.new (); + + if (!ret->ndk_media_format) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to allocate AMediaFormat"); + + g_free (ret); + return NULL; + } + + return ret; +} + +#define MEDIAFORMAT_KEY_MIME "mime" +#define MEDIAFORMAT_SAMPLE_RATE "sample-rate" +#define MEDIAFORMAT_CHANNEL_COUNT "channel-count" + +static GstAmcFormat * +gst_amc_format_ndk_new_audio (const gchar * mime, gint sample_rate, + gint channels, GError ** err) +{ + GstAmcFormat *ret = gst_amc_format_ndk_new (err); + + if (!ret) + return ret; + + a_media_format.set_string (ret->ndk_media_format, MEDIAFORMAT_KEY_MIME, mime); + a_media_format.set_int32 (ret->ndk_media_format, MEDIAFORMAT_SAMPLE_RATE, + sample_rate); + a_media_format.set_int32 (ret->ndk_media_format, MEDIAFORMAT_CHANNEL_COUNT, + channels); + + return ret; +} + +#define MEDIAFORMAT_KEY_WIDTH "width" +#define MEDIAFORMAT_KEY_HEIGHT "height" + +static GstAmcFormat * +gst_amc_format_ndk_new_video (const gchar * mime, gint width, gint height, + GError ** err) +{ + GstAmcFormat *ret = gst_amc_format_ndk_new (err); + + if (!ret) + return ret; + + a_media_format.set_string (ret->ndk_media_format, MEDIAFORMAT_KEY_MIME, mime); + a_media_format.set_int32 (ret->ndk_media_format, MEDIAFORMAT_KEY_WIDTH, + width); + a_media_format.set_int32 (ret->ndk_media_format, MEDIAFORMAT_KEY_HEIGHT, + height); + + return ret; +} + +GstAmcFormat * +gst_amc_format_ndk_from_a_media_format (AMediaFormat * ndk_media_format) +{ + GstAmcFormat *ret = g_new0 (GstAmcFormat, 1); + ret->ndk_media_format = ndk_media_format; + return ret; +} + +static void +gst_amc_format_ndk_free (GstAmcFormat * format) +{ + a_media_format.delete (format->ndk_media_format); + g_free (format); +} + +static gchar * +gst_amc_format_ndk_to_string (GstAmcFormat * format, GError ** err) +{ + const char *str = a_media_format.to_string (format->ndk_media_format); + + if (!str) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to call AMediaFormat_toString()"); + return NULL; + } + + return g_strdup (str); +} + +static gboolean +gst_amc_format_ndk_get_float (GstAmcFormat * format, const gchar * key, + gfloat * value, GError ** err) +{ + float val; + + if (!a_media_format.get_float (format->ndk_media_format, key, &val)) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to call AMediaFormat_getFloat()"); + + return FALSE; + } + + *value = val; + return TRUE; +} + +static gboolean +gst_amc_format_ndk_set_float (GstAmcFormat * format, const gchar * key, + gfloat value, GError ** err) +{ + a_media_format.set_float (format->ndk_media_format, key, value); + return TRUE; +} + +static gboolean +gst_amc_format_ndk_get_int (GstAmcFormat * format, const gchar * key, + gint * value, GError ** err) +{ + int32_t val; + + if (!a_media_format.get_int32 (format->ndk_media_format, key, &val)) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to call AMediaFormat_getInt32()"); + + return FALSE; + } + + *value = val; + return TRUE; +} + +static gboolean +gst_amc_format_ndk_set_int (GstAmcFormat * format, const gchar * key, + gint value, GError ** err) +{ + a_media_format.set_int32 (format->ndk_media_format, key, value); + return TRUE; +} + +static gboolean +gst_amc_format_ndk_get_string (GstAmcFormat * format, const gchar * key, + gchar ** value, GError ** err) +{ + const char *val; + + if (!a_media_format.get_string (format->ndk_media_format, key, &val)) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to call AMediaFormat_getString()"); + + return FALSE; + } + + *value = g_strdup (val); + return TRUE; +} + +static gboolean +gst_amc_format_ndk_set_string (GstAmcFormat * format, const gchar * key, + const gchar * value, GError ** err) +{ + a_media_format.set_string (format->ndk_media_format, key, value); + return TRUE; +} + +static gboolean +gst_amc_format_ndk_get_buffer (GstAmcFormat * format, const gchar * key, + guint8 ** out_data, gsize * out_size, GError ** err) +{ + void *data; + size_t size; + + if (!a_media_format.get_buffer (format->ndk_media_format, key, &data, &size)) { + g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, + "Failed to call AMediaFormat_getBuffer()"); + + return FALSE; + } + + *out_size = size; + *out_data = g_memdup2 (data, size); + return TRUE; +} + +static gboolean +gst_amc_format_ndk_set_buffer (GstAmcFormat * format, const gchar * key, + guint8 * data, gsize size, GError ** err) +{ + a_media_format.set_buffer (format->ndk_media_format, key, data, size); + return TRUE; +} + +GstAmcFormatVTable gst_amc_format_ndk_vtable = { + .new_audio = gst_amc_format_ndk_new_audio, + .new_video = gst_amc_format_ndk_new_video, + .free = gst_amc_format_ndk_free, + + .to_string = gst_amc_format_ndk_to_string, + + .get_float = gst_amc_format_ndk_get_float, + .set_float = gst_amc_format_ndk_set_float, + .get_int = gst_amc_format_ndk_get_int, + .set_int = gst_amc_format_ndk_set_int, + .get_string = gst_amc_format_ndk_get_string, + .set_string = gst_amc_format_ndk_set_string, + .get_buffer = gst_amc_format_ndk_get_buffer, + .set_buffer = gst_amc_format_ndk_set_buffer, +}; diff --git a/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-internal-ndk.h b/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-internal-ndk.h new file mode 100644 index 0000000000..878b07ab22 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-internal-ndk.h @@ -0,0 +1,35 @@ +/* + * 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 + * + */ + +#include "../gstamc-format.h" + +#include +#include + +struct _GstAmcFormat +{ + AMediaFormat * ndk_media_format; +}; + +GstAmcFormat * +gst_amc_format_ndk_new(GError **err); +GstAmcFormat * +gst_amc_format_ndk_from_a_media_format (AMediaFormat * ndk_media_format); diff --git a/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-ndk.h b/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-ndk.h new file mode 100644 index 0000000000..8d41970604 --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/androidmedia/ndk/gstamc-ndk.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023, Ratchanan Srirattanamet + * + * 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 + * + */ + +#include "../gstamc-codec.h" +#include "../gstamc-format.h" + +extern GstAmcCodecVTable gst_amc_codec_ndk_vtable; +extern GstAmcFormatVTable gst_amc_format_ndk_vtable; + +gboolean gst_amc_format_ndk_static_init (void); +gboolean gst_amc_codec_ndk_static_init (void);