You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
604 lines
20 KiB
604 lines
20 KiB
/*****************************************************************************
|
|
* 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"
|
|
|
|
char* MediaCodec_GetName(vlc_object_t *p_obj, const char *psz_mime,
|
|
size_t h264_profile);
|
|
|
|
#define THREAD_NAME "mediacodec_ndk"
|
|
|
|
/* Not in NdkMedia API but we need it since we send config data via input
|
|
* buffers and not via "csd-*" buffers from AMediaFormat */
|
|
#define AMEDIACODEC_FLAG_CODEC_CONFIG 2
|
|
|
|
/*****************************************************************************
|
|
* 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_createCodecByName)(const char *name);
|
|
|
|
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_createCodecByName createCodecByName;
|
|
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;
|
|
|
|
struct members
|
|
{
|
|
const char *name;
|
|
int offset;
|
|
bool critical;
|
|
};
|
|
static struct members members[] =
|
|
{
|
|
#define OFF(x) offsetof(struct syms, AMediaCodec.x)
|
|
{ "AMediaCodec_createCodecByName", OFF(createCodecByName), 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;
|
|
}
|
|
|
|
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;
|
|
AMediaCodecBufferInfo info;
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* Stop
|
|
*****************************************************************************/
|
|
static int Stop(mc_api *api)
|
|
{
|
|
mc_api_sys *p_sys = api->p_sys;
|
|
|
|
api->b_direct_rendering = false;
|
|
|
|
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;
|
|
}
|
|
|
|
msg_Dbg(api->p_obj, "MediaCodec via NDK closed");
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Start
|
|
*****************************************************************************/
|
|
static int Start(mc_api *api, union mc_api_args *p_args)
|
|
{
|
|
mc_api_sys *p_sys = api->p_sys;
|
|
int i_ret = MC_API_ERROR;
|
|
ANativeWindow *p_anw = NULL;
|
|
|
|
assert(api->psz_mime && api->psz_name);
|
|
|
|
p_sys->p_codec = syms.AMediaCodec.createCodecByName(api->psz_name);
|
|
if (!p_sys->p_codec)
|
|
{
|
|
msg_Err(api->p_obj, "AMediaCodec.createCodecByName for %s failed",
|
|
api->psz_name);
|
|
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.setInt32(p_sys->p_format, "encoder", 0);
|
|
syms.AMediaFormat.setString(p_sys->p_format, "mime", api->psz_mime);
|
|
/* No limits for input size */
|
|
syms.AMediaFormat.setInt32(p_sys->p_format, "max-input-size", 0);
|
|
if (api->i_cat == VIDEO_ES)
|
|
{
|
|
syms.AMediaFormat.setInt32(p_sys->p_format, "width", p_args->video.i_width);
|
|
syms.AMediaFormat.setInt32(p_sys->p_format, "height", p_args->video.i_height);
|
|
syms.AMediaFormat.setInt32(p_sys->p_format, "rotation-degrees", p_args->video.i_angle);
|
|
if (p_args->video.p_awh)
|
|
{
|
|
p_anw = AWindowHandler_getANativeWindow(p_args->video.p_awh,
|
|
AWindow_Video);
|
|
if( p_anw )
|
|
syms.AMediaFormat.setInt32(p_sys->p_format,
|
|
"feature-tunneled-playback",
|
|
p_args->video.b_tunneled_playback);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
syms.AMediaFormat.setInt32(p_sys->p_format, "sample-rate", p_args->audio.i_sample_rate);
|
|
syms.AMediaFormat.setInt32(p_sys->p_format, "channel-count", p_args->audio.i_channel_count);
|
|
}
|
|
|
|
if (syms.AMediaCodec.configure(p_sys->p_codec, p_sys->p_format,
|
|
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_anw;
|
|
i_ret = 0;
|
|
|
|
msg_Dbg(api->p_obj, "MediaCodec via NDK opened");
|
|
error:
|
|
if (i_ret != 0)
|
|
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 0;
|
|
else
|
|
return MC_API_ERROR;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* DequeueInput
|
|
*****************************************************************************/
|
|
static int DequeueInput(mc_api *api, mtime_t i_timeout)
|
|
{
|
|
mc_api_sys *p_sys = api->p_sys;
|
|
ssize_t i_index;
|
|
|
|
i_index = syms.AMediaCodec.dequeueInputBuffer(p_sys->p_codec, i_timeout);
|
|
if (i_index >= 0)
|
|
return i_index;
|
|
else if (i_index == AMEDIACODEC_INFO_TRY_AGAIN_LATER)
|
|
return MC_API_INFO_TRYAGAIN;
|
|
else
|
|
{
|
|
msg_Err(api->p_obj, "AMediaCodec.dequeueInputBuffer failed");
|
|
return MC_API_ERROR;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* QueueInput
|
|
*****************************************************************************/
|
|
static int QueueInput(mc_api *api, int i_index, const void *p_buf,
|
|
size_t i_size, mtime_t i_ts, bool b_config)
|
|
{
|
|
mc_api_sys *p_sys = api->p_sys;
|
|
uint8_t *p_mc_buf;
|
|
size_t i_mc_size;
|
|
int i_flags = (b_config ? AMEDIACODEC_FLAG_CODEC_CONFIG : 0)
|
|
| (p_buf == NULL ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
|
|
|
|
assert(i_index >= 0);
|
|
|
|
p_mc_buf = syms.AMediaCodec.getInputBuffer(p_sys->p_codec,
|
|
i_index, &i_mc_size);
|
|
if (!p_mc_buf)
|
|
return MC_API_ERROR;
|
|
|
|
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, i_flags) == AMEDIA_OK)
|
|
return 0;
|
|
else
|
|
{
|
|
msg_Err(api->p_obj, "AMediaCodec.queueInputBuffer failed");
|
|
return MC_API_ERROR;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* DequeueOutput
|
|
*****************************************************************************/
|
|
static int DequeueOutput(mc_api *api, mtime_t i_timeout)
|
|
{
|
|
mc_api_sys *p_sys = api->p_sys;
|
|
ssize_t i_index;
|
|
|
|
i_index = syms.AMediaCodec.dequeueOutputBuffer(p_sys->p_codec, &p_sys->info,
|
|
i_timeout);
|
|
|
|
if (i_index >= 0)
|
|
return i_index;
|
|
else if (i_index == AMEDIACODEC_INFO_TRY_AGAIN_LATER)
|
|
return MC_API_INFO_TRYAGAIN;
|
|
else if (i_index == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)
|
|
return MC_API_INFO_OUTPUT_BUFFERS_CHANGED;
|
|
else if (i_index == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED)
|
|
return MC_API_INFO_OUTPUT_FORMAT_CHANGED;
|
|
else
|
|
{
|
|
msg_Err(api->p_obj, "AMediaCodec.dequeueOutputBuffer failed");
|
|
return MC_API_ERROR;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* GetOutput
|
|
*****************************************************************************/
|
|
static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
|
|
{
|
|
mc_api_sys *p_sys = api->p_sys;
|
|
|
|
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 = p_sys->info.presentationTimeUs;
|
|
p_out->b_eos = p_sys->info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
|
|
|
|
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);
|
|
/* p_mc_buf can be NULL in case of EOS */
|
|
if (!p_mc_buf && !p_out->b_eos)
|
|
{
|
|
msg_Err(api->p_obj, "AMediaCodec.getOutputBuffer failed");
|
|
return MC_API_ERROR;
|
|
}
|
|
p_out->u.buf.p_ptr = p_mc_buf + p_sys->info.offset;
|
|
p_out->u.buf.i_size = p_sys->info.size;
|
|
}
|
|
return 1;
|
|
}
|
|
else if (i_index == MC_API_INFO_OUTPUT_FORMAT_CHANGED)
|
|
{
|
|
AMediaFormat *format = syms.AMediaCodec.getOutputFormat(p_sys->p_codec);
|
|
|
|
p_out->type = MC_OUT_TYPE_CONF;
|
|
p_out->b_eos = false;
|
|
if (api->i_cat == VIDEO_ES)
|
|
{
|
|
p_out->u.conf.video.width = GetFormatInteger(format, "width");
|
|
p_out->u.conf.video.height = GetFormatInteger(format, "height");
|
|
p_out->u.conf.video.stride = GetFormatInteger(format, "stride");
|
|
p_out->u.conf.video.slice_height = GetFormatInteger(format, "slice-height");
|
|
p_out->u.conf.video.pixel_format = GetFormatInteger(format, "color-format");
|
|
p_out->u.conf.video.crop_left = GetFormatInteger(format, "crop-left");
|
|
p_out->u.conf.video.crop_top = GetFormatInteger(format, "crop-top");
|
|
p_out->u.conf.video.crop_right = GetFormatInteger(format, "crop-right");
|
|
p_out->u.conf.video.crop_bottom = GetFormatInteger(format, "crop-bottom");
|
|
}
|
|
else
|
|
{
|
|
p_out->u.conf.audio.channel_count = GetFormatInteger(format, "channel-count");
|
|
p_out->u.conf.audio.channel_mask = GetFormatInteger(format, "channel-mask");
|
|
p_out->u.conf.audio.sample_rate = GetFormatInteger(format, "sample-rate");
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* ReleaseOutput
|
|
*****************************************************************************/
|
|
static int ReleaseOutput(mc_api *api, int i_index, bool b_render)
|
|
{
|
|
mc_api_sys *p_sys = api->p_sys;
|
|
|
|
assert(i_index >= 0);
|
|
if (syms.AMediaCodec.releaseOutputBuffer(p_sys->p_codec, i_index, b_render)
|
|
== AMEDIA_OK)
|
|
return 0;
|
|
else
|
|
return MC_API_ERROR;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Clean
|
|
*****************************************************************************/
|
|
static void Clean(mc_api *api)
|
|
{
|
|
free(api->psz_name);
|
|
free(api->p_sys);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Configure
|
|
*****************************************************************************/
|
|
static int Configure(mc_api * api, size_t i_h264_profile)
|
|
{
|
|
free(api->psz_name);
|
|
api->psz_name = MediaCodec_GetName(api->p_obj, api->psz_mime,
|
|
i_h264_profile);
|
|
if (!api->psz_name)
|
|
return MC_API_ERROR;
|
|
api->i_quirks = OMXCodec_GetQuirks(api->i_cat, api->i_codec, api->psz_name,
|
|
strlen(api->psz_name));
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* MediaCodecNdk_Init
|
|
*****************************************************************************/
|
|
int MediaCodecNdk_Init(mc_api *api)
|
|
{
|
|
if (!InitSymbols(api))
|
|
return MC_API_ERROR;
|
|
|
|
api->p_sys = calloc(1, sizeof(mc_api_sys));
|
|
if (!api->p_sys)
|
|
return MC_API_ERROR;
|
|
|
|
api->clean = Clean;
|
|
api->configure = Configure;
|
|
api->start = Start;
|
|
api->stop = Stop;
|
|
api->flush = Flush;
|
|
api->dequeue_in = DequeueInput;
|
|
api->queue_in = QueueInput;
|
|
api->dequeue_out = DequeueOutput;
|
|
api->get_out = GetOutput;
|
|
api->release_out = ReleaseOutput;
|
|
|
|
api->b_support_interlaced = true;
|
|
return 0;
|
|
}
|
|
|