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