androidmedia: add NDK implementation of Android MediaCodec
This reduces the amount of Java <-> native calls, which should reduce overhead a bit. It also paves a way to share the code between Android and a libhybris-based backend (where GNU/Linux system uses Android driver) in the future. Bug: https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/1242 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4115>
This commit is contained in:
parent
a50ce9c6b0
commit
facb000afe
@ -17,23 +17,84 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <android/api-level.h>
|
||||||
|
|
||||||
#include "../gstamc.h"
|
#include "../gstamc.h"
|
||||||
#include "gstamc-jni.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
|
gboolean
|
||||||
gst_amc_static_init (void)
|
gst_amc_static_init (void)
|
||||||
{
|
{
|
||||||
if (!gst_amc_codeclist_jni_static_init ())
|
if (!gst_amc_codeclist_jni_static_init ())
|
||||||
return FALSE;
|
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 ())
|
if (!gst_amc_codec_jni_static_init ())
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!gst_amc_format_jni_static_init ())
|
if (!gst_amc_format_jni_static_init ())
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!gst_amc_surface_texture_jni_static_init ())
|
GST_INFO ("Use JNI implementation for GstAmc.");
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
gst_amc_format_vtable = &gst_amc_format_jni_vtable;
|
gst_amc_format_vtable = &gst_amc_format_jni_vtable;
|
||||||
gst_amc_codec_vtable = &gst_amc_codec_jni_vtable;
|
gst_amc_codec_vtable = &gst_amc_codec_jni_vtable;
|
||||||
|
@ -75,6 +75,19 @@ else
|
|||||||
'jni/gstamcsurface.c',
|
'jni/gstamcsurface.c',
|
||||||
'jni/gstamcsurfacetexture-jni.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'
|
plugin_name = 'gstandroidmedia'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -0,0 +1,584 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012,2018 Collabora Ltd.
|
||||||
|
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||||
|
* Copyright (C) 2015, Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
* Copyright (C) 2023, Ratchanan Srirattanamet <peathot@hotmail.com>
|
||||||
|
*
|
||||||
|
* 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 <android/native_window.h>
|
||||||
|
#include <android/native_window_jni.h>
|
||||||
|
#include <media/NdkMediaError.h>
|
||||||
|
#include <media/NdkMediaCodec.h>
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
@ -0,0 +1,310 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012,2018 Collabora Ltd.
|
||||||
|
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||||
|
* Copyright (C) 2015, Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
* Copyright (C) 2023, Ratchanan Srirattanamet <peathot@hotmail.com>
|
||||||
|
*
|
||||||
|
* 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 <media/NdkMediaFormat.h>
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012,2018 Collabora Ltd.
|
||||||
|
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||||
|
* Copyright (C) 2015, Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
*
|
||||||
|
* 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 <stdbool.h>
|
||||||
|
#include <media/NdkMediaFormat.h>
|
||||||
|
|
||||||
|
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);
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023, Ratchanan Srirattanamet <peathot@hotmail.com>
|
||||||
|
*
|
||||||
|
* 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);
|
Loading…
x
Reference in New Issue
Block a user