From 286fa11d5024d6b35063adb950c8f1b9f7d6a9e9 Mon Sep 17 00:00:00 2001 From: Thomas Guillem <thomas@gllm.fr> Date: Wed, 20 May 2015 14:33:33 +0200 Subject: [PATCH] mediacodec: add NDK module The JNI module is now a submodule. --- modules/codec/Makefile.am | 2 +- modules/codec/omxil/mediacodec.c | 14 +- modules/codec/omxil/mediacodec.h | 1 + modules/codec/omxil/mediacodec_ndk.c | 545 +++++++++++++++++++++++++++ 4 files changed, 559 insertions(+), 3 deletions(-) create mode 100644 modules/codec/omxil/mediacodec_ndk.c diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am index 4272d03cc31c..fa97f5372526 100644 --- a/modules/codec/Makefile.am +++ b/modules/codec/Makefile.am @@ -398,7 +398,7 @@ libiomx_plugin_la_LIBADD = $(libomxil_plugin_la_LIBADD) libmediacodec_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/codec/omxil libmediacodec_plugin_la_SOURCES = codec/omxil/mediacodec.c codec/omxil/mediacodec.h \ - codec/omxil/mediacodec_jni.c codec/omxil/utils.c \ + codec/omxil/mediacodec_jni.c codec/omxil/mediacodec_ndk.c codec/omxil/utils.c \ video_chroma/copy.c codec/omxil/android_opaque.c codec/omxil/android_opaque.h \ packetizer/h264_nal.c packetizer/h264_nal.h packetizer/hevc_nal.c packetizer/hevc_nal.h diff --git a/modules/codec/omxil/mediacodec.c b/modules/codec/omxil/mediacodec.c index ce4034a35500..ac03db71a5c5 100644 --- a/modules/codec/omxil/mediacodec.c +++ b/modules/codec/omxil/mediacodec.c @@ -173,6 +173,7 @@ struct decoder_sys_t * Local prototypes *****************************************************************************/ static int OpenDecoderJni(vlc_object_t *); +static int OpenDecoderNdk(vlc_object_t *); static void CloseDecoder(vlc_object_t *); static picture_t *DecodeVideo(decoder_t *, block_t **); @@ -197,8 +198,12 @@ vlc_module_begin () set_capability( "decoder", 0 ) /* Only enabled via commandline arguments */ add_bool(CFG_PREFIX "dr", true, DIRECTRENDERING_TEXT, DIRECTRENDERING_LONGTEXT, true) - set_callbacks( OpenDecoderJni, CloseDecoder ) - add_shortcut( "mediacodec_jni" ) + set_callbacks( OpenDecoderNdk, CloseDecoder ) + add_shortcut( "mediacodec_ndk" ) + add_submodule () + set_capability( "decoder", 0 ) + set_callbacks( OpenDecoderJni, CloseDecoder ) + add_shortcut( "mediacodec_jni" ) vlc_module_end () @@ -542,6 +547,11 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init) return StartMediaCodec(p_dec); } +static int OpenDecoderNdk(vlc_object_t *p_this) +{ + return OpenDecoder(p_this, MediaCodecNdk_Init); +} + static int OpenDecoderJni(vlc_object_t *p_this) { return OpenDecoder(p_this, MediaCodecJni_Init); diff --git a/modules/codec/omxil/mediacodec.h b/modules/codec/omxil/mediacodec.h index 372b84d9c91c..551f4f0990f3 100644 --- a/modules/codec/omxil/mediacodec.h +++ b/modules/codec/omxil/mediacodec.h @@ -30,6 +30,7 @@ typedef struct mc_api_out mc_api_out; typedef int (*pf_MediaCodecApi_init)(mc_api*); int MediaCodecJni_Init(mc_api*); +int MediaCodecNdk_Init(mc_api*); struct mc_api_out { diff --git a/modules/codec/omxil/mediacodec_ndk.c b/modules/codec/omxil/mediacodec_ndk.c new file mode 100644 index 000000000000..2d69963d330b --- /dev/null +++ b/modules/codec/omxil/mediacodec_ndk.c @@ -0,0 +1,545 @@ +/***************************************************************************** + * mediacodec_ndk.c: mc_api implementation using NDK + ***************************************************************************** + * Copyright © 2015 VLC authors and VideoLAN, VideoLabs + * + * This program 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; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <jni.h> +#include <dlfcn.h> +#include <stdint.h> +#include <assert.h> + +#include <vlc_common.h> + +#include <OMX_Core.h> +#include <OMX_Component.h> +#include "omxil_utils.h" + +#include "mediacodec.h" +#include "../../video_output/android/utils.h" + +#define THREAD_NAME "mediacodec_ndk" +extern JNIEnv *jni_get_env(const char *name); + +/***************************************************************************** + * NdkMediaError.h + *****************************************************************************/ + +typedef enum { + AMEDIA_OK = 0, + + AMEDIA_ERROR_BASE = -10000, + AMEDIA_ERROR_UNKNOWN = AMEDIA_ERROR_BASE, + AMEDIA_ERROR_MALFORMED = AMEDIA_ERROR_BASE - 1, + AMEDIA_ERROR_UNSUPPORTED = AMEDIA_ERROR_BASE - 2, + AMEDIA_ERROR_INVALID_OBJECT = AMEDIA_ERROR_BASE - 3, + AMEDIA_ERROR_INVALID_PARAMETER = AMEDIA_ERROR_BASE - 4, + + AMEDIA_DRM_ERROR_BASE = -20000, + AMEDIA_DRM_NOT_PROVISIONED = AMEDIA_DRM_ERROR_BASE - 1, + AMEDIA_DRM_RESOURCE_BUSY = AMEDIA_DRM_ERROR_BASE - 2, + AMEDIA_DRM_DEVICE_REVOKED = AMEDIA_DRM_ERROR_BASE - 3, + AMEDIA_DRM_SHORT_BUFFER = AMEDIA_DRM_ERROR_BASE - 4, + AMEDIA_DRM_SESSION_NOT_OPENED = AMEDIA_DRM_ERROR_BASE - 5, + AMEDIA_DRM_TAMPER_DETECTED = AMEDIA_DRM_ERROR_BASE - 6, + AMEDIA_DRM_VERIFY_FAILED = AMEDIA_DRM_ERROR_BASE - 7, + AMEDIA_DRM_NEED_KEY = AMEDIA_DRM_ERROR_BASE - 8, + AMEDIA_DRM_LICENSE_EXPIRED = AMEDIA_DRM_ERROR_BASE - 9, + +} media_status_t; + +/***************************************************************************** + * NdkMediaCodec.h + *****************************************************************************/ + +struct AMediaCodec; +typedef struct AMediaCodec AMediaCodec; + +struct AMediaCodecBufferInfo { + int32_t offset; + int32_t size; + int64_t presentationTimeUs; + uint32_t flags; +}; +typedef struct AMediaCodecBufferInfo AMediaCodecBufferInfo; + +enum { + AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM = 4, + AMEDIACODEC_CONFIGURE_FLAG_ENCODE = 1, + AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED = -3, + AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED = -2, + AMEDIACODEC_INFO_TRY_AGAIN_LATER = -1 +}; + +struct AMediaFormat; +typedef struct AMediaFormat AMediaFormat; + +struct AMediaCrypto; +typedef struct AMediaCrypto AMediaCrypto; + +/***************************************************************************** + * Ndk symbols + *****************************************************************************/ + +typedef AMediaCodec* (*pf_AMediaCodec_createDecoderByType)(const char *mime_type); + +typedef media_status_t (*pf_AMediaCodec_configure)(AMediaCodec*, + const AMediaFormat* format, + ANativeWindow* surface, + AMediaCrypto *crypto, + uint32_t flags); + +typedef media_status_t (*pf_AMediaCodec_start)(AMediaCodec*); + +typedef media_status_t (*pf_AMediaCodec_stop)(AMediaCodec*); + +typedef media_status_t (*pf_AMediaCodec_flush)(AMediaCodec*); + +typedef media_status_t (*pf_AMediaCodec_delete)(AMediaCodec*); + +typedef AMediaFormat* (*pf_AMediaCodec_getOutputFormat)(AMediaCodec*); + +typedef ssize_t (*pf_AMediaCodec_dequeueInputBuffer)(AMediaCodec*, + int64_t timeoutUs); + +typedef uint8_t* (*pf_AMediaCodec_getInputBuffer)(AMediaCodec*, + size_t idx, size_t *out_size); + +typedef media_status_t (*pf_AMediaCodec_queueInputBuffer)(AMediaCodec*, + size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags); + +typedef ssize_t (*pf_AMediaCodec_dequeueOutputBuffer)(AMediaCodec*, + AMediaCodecBufferInfo *info, int64_t timeoutUs); + +typedef uint8_t* (*pf_AMediaCodec_getOutputBuffer)(AMediaCodec*, + size_t idx, size_t *out_size); + +typedef media_status_t (*pf_AMediaCodec_releaseOutputBuffer)(AMediaCodec*, + size_t idx, bool render); + +typedef AMediaFormat *(*pf_AMediaFormat_new)(); +typedef media_status_t (*pf_AMediaFormat_delete)(AMediaFormat*); + +typedef void (*pf_AMediaFormat_setString)(AMediaFormat*, + const char* name, const char* value); + +typedef void (*pf_AMediaFormat_setInt32)(AMediaFormat*, + const char* name, int32_t value); + +typedef bool (*pf_AMediaFormat_getInt32)(AMediaFormat*, + const char *name, int32_t *out); + +struct syms +{ + struct { + pf_AMediaCodec_createDecoderByType createDecoderByType; + pf_AMediaCodec_configure configure; + pf_AMediaCodec_start start; + pf_AMediaCodec_stop stop; + pf_AMediaCodec_flush flush; + pf_AMediaCodec_delete delete; + pf_AMediaCodec_getOutputFormat getOutputFormat; + pf_AMediaCodec_dequeueInputBuffer dequeueInputBuffer; + pf_AMediaCodec_getInputBuffer getInputBuffer; + pf_AMediaCodec_queueInputBuffer queueInputBuffer; + pf_AMediaCodec_dequeueOutputBuffer dequeueOutputBuffer; + pf_AMediaCodec_getOutputBuffer getOutputBuffer; + pf_AMediaCodec_releaseOutputBuffer releaseOutputBuffer; + } AMediaCodec; + struct { + pf_AMediaFormat_new new; + pf_AMediaFormat_delete delete; + pf_AMediaFormat_setString setString; + pf_AMediaFormat_setInt32 setInt32; + pf_AMediaFormat_getInt32 getInt32; + } AMediaFormat; +}; +static struct syms syms; +static native_window_api_t anw_syms; + +struct members +{ + const char *name; + int offset; + bool critical; +}; +static struct members members[] = +{ +#define OFF(x) offsetof(struct syms, AMediaCodec.x) + { "AMediaCodec_createDecoderByType", OFF(createDecoderByType), true }, + { "AMediaCodec_configure", OFF(configure), true }, + { "AMediaCodec_start", OFF(start), true }, + { "AMediaCodec_stop", OFF(stop), true }, + { "AMediaCodec_flush", OFF(flush), true }, + { "AMediaCodec_delete", OFF(delete), true }, + { "AMediaCodec_getOutputFormat", OFF(getOutputFormat), true }, + { "AMediaCodec_dequeueInputBuffer", OFF(dequeueInputBuffer), true }, + { "AMediaCodec_getInputBuffer", OFF(getInputBuffer), true }, + { "AMediaCodec_queueInputBuffer", OFF(queueInputBuffer), true }, + { "AMediaCodec_dequeueOutputBuffer", OFF(dequeueOutputBuffer), true }, + { "AMediaCodec_getOutputBuffer", OFF(getOutputBuffer), true }, + { "AMediaCodec_releaseOutputBuffer", OFF(releaseOutputBuffer), true }, +#undef OFF +#define OFF(x) offsetof(struct syms, AMediaFormat.x) + { "AMediaFormat_new", OFF(new), true }, + { "AMediaFormat_delete", OFF(delete), true }, + { "AMediaFormat_setString", OFF(setString), true }, + { "AMediaFormat_setInt32", OFF(setInt32), true }, + { "AMediaFormat_getInt32", OFF(getInt32), true }, +#undef OFF + { NULL, 0, false } +}; +#undef OFF + +/* Initialize all symbols. + * Done only one time during the first initialisation */ +static bool +InitSymbols(mc_api *api) +{ + static vlc_mutex_t lock = VLC_STATIC_MUTEX; + static int i_init_state = -1; + bool ret; + + vlc_mutex_lock(&lock); + + if (i_init_state != -1) + goto end; + + i_init_state = 0; + + void *ndk_handle = dlopen("libmediandk.so", RTLD_NOW); + if (!ndk_handle) + goto end; + + for (int i = 0; members[i].name; i++) + { + void *sym = dlsym(ndk_handle, members[i].name); + if (!sym && members[i].critical) + { + dlclose(ndk_handle); + goto end; + } + *(void **)((uint8_t*)&syms + members[i].offset) = sym; + } + void *anw_handle = LoadNativeWindowAPI(&anw_syms); + if (!anw_handle) + { + dlclose(ndk_handle); + goto end; + } + + i_init_state = 1; +end: + ret = i_init_state == 1; + if (!ret) + msg_Err(api->p_obj, "MediaCodec NDK init failed"); + + vlc_mutex_unlock(&lock); + return ret; +} + +/**************************************************************************** + * Local prototypes + ****************************************************************************/ + +struct mc_api_sys +{ + AMediaCodec* p_codec; + AMediaFormat* p_format; + ANativeWindow* p_anw; +}; + +/***************************************************************************** + * Stop + *****************************************************************************/ +static int Stop(mc_api *api) +{ + mc_api_sys *p_sys = api->p_sys; + + api->b_direct_rendering = false; + api->b_support_interlaced = false; + api->psz_name = NULL; + + if (p_sys->p_codec) + { + if (api->b_started) + { + syms.AMediaCodec.stop(p_sys->p_codec); + api->b_started = false; + } + syms.AMediaCodec.delete(p_sys->p_codec); + p_sys->p_codec = NULL; + } + if (p_sys->p_format) + { + syms.AMediaFormat.delete(p_sys->p_format); + p_sys->p_format = NULL; + } + if (p_sys->p_anw) + { + anw_syms.winRelease(p_sys->p_anw); + p_sys->p_anw = NULL; + } + + msg_Dbg(api->p_obj, "MediaCodec via NDK closed"); + return VLC_SUCCESS; +} + +/***************************************************************************** + * Start + *****************************************************************************/ +static int Start(mc_api *api, jobject jsurface, const char *psz_mime, + int i_width, int i_height, size_t h264_profile, int i_angle) +{ + mc_api_sys *p_sys = api->p_sys; + int i_ret = VLC_EGENERIC; + (void) h264_profile; + + p_sys->p_codec = syms.AMediaCodec.createDecoderByType(psz_mime); + if (!p_sys->p_codec) + { + msg_Err(api->p_obj, "AMediaCodec.createDecoderByType for %s failed", psz_mime); + goto error; + } + + p_sys->p_format = syms.AMediaFormat.new(); + if (!p_sys->p_format) + { + msg_Err(api->p_obj, "AMediaFormat.new failed"); + goto error; + } + + syms.AMediaFormat.setString(p_sys->p_format, "mime", psz_mime); + syms.AMediaFormat.setInt32(p_sys->p_format, "width", i_width); + syms.AMediaFormat.setInt32(p_sys->p_format, "height", i_height); + syms.AMediaFormat.setInt32(p_sys->p_format, "rotation-degrees", i_angle); + + if (jsurface) + { + JNIEnv *env; + + if (!(env = jni_get_env(THREAD_NAME))) + goto error; + p_sys->p_anw = anw_syms.winFromSurface(env, jsurface); + } + + if (syms.AMediaCodec.configure(p_sys->p_codec, p_sys->p_format, + p_sys->p_anw, NULL, 0) != AMEDIA_OK) + { + msg_Err(api->p_obj, "AMediaCodec.configure failed"); + goto error; + } + if (syms.AMediaCodec.start(p_sys->p_codec) != AMEDIA_OK) + { + msg_Err(api->p_obj, "AMediaCodec.start failed"); + goto error; + } + + api->b_started = true; + api->b_direct_rendering = !!p_sys->p_anw; + api->b_support_interlaced = true; + api->psz_name = ""; // TODO + i_ret = VLC_SUCCESS; + + msg_Dbg(api->p_obj, "MediaCodec via NDK opened"); +error: + if (i_ret != VLC_SUCCESS) + Stop(api); + return i_ret; +} + +/***************************************************************************** + * Flush + *****************************************************************************/ +static int Flush(mc_api *api) +{ + mc_api_sys *p_sys = api->p_sys; + + if (syms.AMediaCodec.flush(p_sys->p_codec) == AMEDIA_OK) + return VLC_SUCCESS; + else + return VLC_EGENERIC; +} + +/***************************************************************************** + * PutInput + *****************************************************************************/ +static int PutInput(mc_api *api, const void *p_buf, size_t i_size, + mtime_t i_ts, bool b_config, mtime_t i_timeout) +{ + mc_api_sys *p_sys = api->p_sys; + ssize_t i_index; + uint8_t *p_mc_buf; + size_t i_mc_size; + (void) b_config; + + i_index = syms.AMediaCodec.dequeueInputBuffer(p_sys->p_codec, i_timeout); + if (i_index < 0) + { + if (i_index == AMEDIACODEC_INFO_TRY_AGAIN_LATER) + return 0; + else + { + msg_Err(api->p_obj, "AMediaCodec.dequeueInputBuffer failed"); + return VLC_EGENERIC; + } + } + + p_mc_buf = syms.AMediaCodec.getInputBuffer(p_sys->p_codec, + i_index, &i_mc_size); + if (!p_mc_buf) + return VLC_EGENERIC; + + if (i_mc_size > i_size) + i_mc_size = i_size; + memcpy(p_mc_buf, p_buf, i_mc_size); + + if (syms.AMediaCodec.queueInputBuffer(p_sys->p_codec, i_index, 0, i_mc_size, + i_ts, 0) == AMEDIA_OK) + return 1; + else + { + msg_Err(api->p_obj, "AMediaCodec.queueInputBuffer failed"); + return VLC_EGENERIC; + } +} + +static int32_t GetFormatInteger(AMediaFormat *p_format, const char *psz_name) +{ + int32_t i_out = 0; + syms.AMediaFormat.getInt32(p_format, psz_name, &i_out); + return i_out; +} + +/***************************************************************************** + * GetOutput + *****************************************************************************/ +static int GetOutput(mc_api *api, mc_api_out *p_out, mtime_t i_timeout) +{ + mc_api_sys *p_sys = api->p_sys; + AMediaCodecBufferInfo info; + ssize_t i_index; + + i_index = syms.AMediaCodec.dequeueOutputBuffer(p_sys->p_codec, &info, + i_timeout); + if (i_index >= 0) + { + p_out->type = MC_OUT_TYPE_BUF; + p_out->u.buf.i_index = i_index; + + p_out->u.buf.i_ts = info.presentationTimeUs; + + if (api->b_direct_rendering) + { + p_out->u.buf.p_ptr = NULL; + p_out->u.buf.i_size = 0; + } + else + { + size_t i_mc_size; + uint8_t *p_mc_buf = syms.AMediaCodec.getOutputBuffer(p_sys->p_codec, + i_index, + &i_mc_size); + if (!p_mc_buf) + { + msg_Err(api->p_obj, "AMediaCodec.getOutputBuffer failed"); + return VLC_EGENERIC; + } + p_out->u.buf.p_ptr = p_mc_buf + info.offset; + p_out->u.buf.i_size = i_mc_size; + } + return 1; + } + else if (i_index == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) + { + AMediaFormat *format = syms.AMediaCodec.getOutputFormat(p_sys->p_codec); + + p_out->type = MC_OUT_TYPE_CONF; + p_out->u.conf.width = GetFormatInteger(format, "width"); + p_out->u.conf.height = GetFormatInteger(format, "height"); + p_out->u.conf.stride = GetFormatInteger(format, "stride"); + p_out->u.conf.slice_height = GetFormatInteger(format, "slice-height"); + p_out->u.conf.pixel_format = GetFormatInteger(format, "color-format"); + p_out->u.conf.crop_left = GetFormatInteger(format, "crop-left"); + p_out->u.conf.crop_top = GetFormatInteger(format, "crop-top"); + p_out->u.conf.crop_right = GetFormatInteger(format, "crop-right"); + p_out->u.conf.crop_bottom = GetFormatInteger(format, "crop-bottom"); + return 1; + } + else if (i_index == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED + || i_index == AMEDIACODEC_INFO_TRY_AGAIN_LATER) + { + return 0; + } + else + { + msg_Err(api->p_obj, "AMediaCodec.dequeueOutputBuffer failed"); + return VLC_EGENERIC; + } +} + +/***************************************************************************** + * ReleaseOutput + *****************************************************************************/ +static int ReleaseOutput(mc_api *api, int i_index, bool b_render) +{ + mc_api_sys *p_sys = api->p_sys; + + if (syms.AMediaCodec.releaseOutputBuffer(p_sys->p_codec, i_index, b_render) + == AMEDIA_OK) + return VLC_SUCCESS; + else + return VLC_EGENERIC; +} + + +/***************************************************************************** + * Clean + *****************************************************************************/ +static void Clean(mc_api *api) +{ + free(api->p_sys); +} + +/***************************************************************************** + * MediaCodecNdk_Init + *****************************************************************************/ +int MediaCodecNdk_Init(mc_api *api) +{ + if (!InitSymbols(api)) + return VLC_EGENERIC; + + api->p_sys = calloc(1, sizeof(mc_api_sys)); + if (!api->p_sys) + return VLC_EGENERIC; + + api->clean = Clean; + api->start = Start; + api->stop = Stop; + api->flush = Flush; + api->put_in = PutInput; + api->get_out = GetOutput; + api->release_out = ReleaseOutput; + return VLC_SUCCESS; +} -- GitLab