Commit 286fa11d authored by Thomas Guillem's avatar Thomas Guillem

mediacodec: add NDK module

The JNI module is now a submodule.
parent cae9e8d8
......@@ -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
......
......@@ -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);
......
......@@ -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
{
......
/*****************************************************************************
* 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment