Commit c7978950 authored by Martin Storsjö's avatar Martin Storsjö Committed by Jean-Baptiste Kempf

omxil: Allow using IOMX on Android

This adds a fake OMX core implementation, relying the calls
via IOMX to the media server, which contains the actual
OMX core.

Building with IOMX requires private Android headers from the
Android source tree, namely the frameworks/base and
system/core repositories. (Either froyo or gingerbread
should work for building.) This API is not public, has no
ABI guarantees and isn't supported.

Linking also requires libraries extracted from a
froyo/gingerbread device or emulator.

Since there are no ABI guarantees, linking to this API might
make the .so fail to load on some devices, so for proper use
it should be in a dynamically loaded module, separate from the
rest of the VLC core and modules.

Since this can lead to crashes on unsupported devices, it should
only be used in production on whitelisted device/firmware
combinations that are known to work.
Signed-off-by: default avatarJean-Baptiste Kempf <jb@videolan.org>
parent 507ebbfe
......@@ -2327,6 +2327,27 @@ then
VLC_ADD_LIBS([omxil], [$LIBDL])
fi
dnl
dnl iomx codec plugin
dnl
AC_ARG_ENABLE(iomx,
[ --enable-iomx iomx codec module (default disabled)])
if test "${enable_iomx}" = "yes"
then
CPPFLAGS_save="${CPPFLAGS}"
CPPFLAGS="${CPPFLAGS} -I${srcdir}/modules/codec/omxil" # For the OMX headers
AC_LANG_PUSH([C++])
AC_CHECK_HEADER(media/stagefright/OMXClient.h, [
VLC_ADD_PLUGIN([iomx])
VLC_ADD_CXXFLAGS([iomx], [-fno-exceptions -fno-rtti])
VLC_ADD_LIBS([iomx], [-lstagefright -lmedia -lutils -lbinder])
], [
AC_MSG_ERROR("Could not find IOMX headers")
])
AC_LANG_POP([C++])
CPPFLAGS="${CPPFLAGS_save}"
fi
dnl
dnl CrystalHD codec plugin
dnl
......
SOURCES_omxil = omxil.c utils.c omxil.h omxil_utils.h \
OMX_Component.h OMX_Core.h OMX_Image.h OMX_IVCommon.h OMX_Types.h \
OMX_Audio.h OMX_Index.h OMX_Other.h OMX_Video.h
CPPFLAGS_iomx = -DUSE_IOMX
SOURCES_iomx = omxil.c utils.c iomx.cpp omxil.h omxil_utils.h iomx.h \
OMX_Component.h OMX_Core.h OMX_Image.h OMX_IVCommon.h OMX_Types.h \
OMX_Audio.h OMX_Index.h OMX_Other.h OMX_Video.h
/*****************************************************************************
* iomx.cpp: OpenMAX interface implementation based on IOMX
*****************************************************************************
* Copyright (C) 2011 VLC authors and VideoLAN
*
* Authors: Martin Storsjo <martin@martin.st>
*
* 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 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 <vlc_common.h>
#include <media/stagefright/OMXClient.h>
#include <media/IOMX.h>
#include <binder/MemoryDealer.h>
#include <OMX_Component.h>
#include "iomx.h"
extern "C" {
#include "omxil_utils.h"
}
using namespace android;
class IOMXContext {
public:
IOMXContext() {
refcount = 0;
init_refcount = 0;
}
sp<IOMX> iomx;
List<IOMX::ComponentInfo> components;
int refcount;
int init_refcount;
};
static IOMXContext *ctx;
class OMXNode;
class OMXCodecObserver : public BnOMXObserver {
public:
OMXCodecObserver() {
node = NULL;
}
void setNode(OMXNode* n) {
node = n;
}
void onMessage(const omx_message &msg);
void registerBuffers(const sp<IMemoryHeap> &mem) {
}
private:
OMXNode *node;
};
class OMXNode {
public:
IOMX::node_id node;
sp<OMXCodecObserver> observer;
OMX_CALLBACKTYPE callbacks;
OMX_PTR app_data;
OMX_STATETYPE state;
List<OMX_BUFFERHEADERTYPE*> buffers;
OMX_HANDLETYPE handle;
String8 component_name;
};
class OMXBuffer {
public:
sp<MemoryDealer> dealer;
IOMX::buffer_id id;
};
void OMXCodecObserver::onMessage(const omx_message &msg)
{
if (!node)
return;
switch (msg.type) {
case omx_message::EVENT:
// TODO: Needs locking
if (msg.u.event_data.event == OMX_EventCmdComplete && msg.u.event_data.data1 == OMX_CommandStateSet)
node->state = (OMX_STATETYPE) msg.u.event_data.data2;
node->callbacks.EventHandler(node->handle, node->app_data, msg.u.event_data.event, msg.u.event_data.data1, msg.u.event_data.data2, NULL);
break;
case omx_message::EMPTY_BUFFER_DONE:
for( List<OMX_BUFFERHEADERTYPE*>::iterator it = node->buffers.begin(); it != node->buffers.end(); it++ ) {
OMXBuffer* info = (OMXBuffer*) (*it)->pPlatformPrivate;
if (msg.u.buffer_data.buffer == info->id) {
node->callbacks.EmptyBufferDone(node->handle, node->app_data, *it);
break;
}
}
break;
case omx_message::FILL_BUFFER_DONE:
for( List<OMX_BUFFERHEADERTYPE*>::iterator it = node->buffers.begin(); it != node->buffers.end(); it++ ) {
OMXBuffer* info = (OMXBuffer*) (*it)->pPlatformPrivate;
if (msg.u.extended_buffer_data.buffer == info->id) {
OMX_BUFFERHEADERTYPE *buffer = *it;
buffer->nOffset = msg.u.extended_buffer_data.range_offset;
buffer->nFilledLen = msg.u.extended_buffer_data.range_length;
buffer->nFlags = msg.u.extended_buffer_data.flags;
buffer->nTimeStamp = msg.u.extended_buffer_data.timestamp;
node->callbacks.FillBufferDone(node->handle, node->app_data, buffer);
break;
}
}
break;
default:
break;
}
}
static OMX_ERRORTYPE get_error(status_t err)
{
if (err == OK)
return OMX_ErrorNone;
return OMX_ErrorUndefined;
}
static int get_param_size(OMX_INDEXTYPE param_index)
{
switch (param_index) {
case OMX_IndexParamPortDefinition:
return sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
case OMX_IndexParamStandardComponentRole:
return sizeof(OMX_PARAM_COMPONENTROLETYPE);
case OMX_IndexParamVideoInit:
case OMX_IndexParamAudioInit:
case OMX_IndexParamImageInit:
case OMX_IndexParamOtherInit:
return sizeof(OMX_PORT_PARAM_TYPE);
case OMX_IndexParamNumAvailableStreams:
return sizeof(OMX_PARAM_U32TYPE);
default:
return GetAudioParamSize(param_index);
}
}
static OMX_ERRORTYPE iomx_send_command(OMX_HANDLETYPE component, OMX_COMMANDTYPE command, OMX_U32 param1, OMX_PTR cmd_data)
{
OMXNode* node = (OMXNode*) ((OMX_COMPONENTTYPE*)component)->pComponentPrivate;
return get_error(ctx->iomx->sendCommand(node->node, command, param1));
}
static OMX_ERRORTYPE iomx_get_parameter(OMX_HANDLETYPE component, OMX_INDEXTYPE param_index, OMX_PTR param)
{
OMXNode* node = (OMXNode*) ((OMX_COMPONENTTYPE*)component)->pComponentPrivate;
return get_error(ctx->iomx->getParameter(node->node, param_index, param, get_param_size(param_index)));
}
static OMX_ERRORTYPE iomx_set_parameter(OMX_HANDLETYPE component, OMX_INDEXTYPE param_index, OMX_PTR param)
{
OMXNode* node = (OMXNode*) ((OMX_COMPONENTTYPE*)component)->pComponentPrivate;
return get_error(ctx->iomx->setParameter(node->node, param_index, param, get_param_size(param_index)));
}
static OMX_ERRORTYPE iomx_get_state(OMX_HANDLETYPE component, OMX_STATETYPE *ptr) {
OMXNode* node = (OMXNode*) ((OMX_COMPONENTTYPE*)component)->pComponentPrivate;
*ptr = node->state;
return OMX_ErrorNone;
}
static OMX_ERRORTYPE iomx_allocate_buffer(OMX_HANDLETYPE component, OMX_BUFFERHEADERTYPE **bufferptr, OMX_U32 port_index, OMX_PTR app_private, OMX_U32 size)
{
OMXNode* node = (OMXNode*) ((OMX_COMPONENTTYPE*)component)->pComponentPrivate;
OMXBuffer* info = new OMXBuffer;
info->dealer = new MemoryDealer(size + 4096); // Do we need to keep this around, or is it kept alive via the IMemory that references it?
sp<IMemory> mem = info->dealer->allocate(size);
int ret = ctx->iomx->allocateBufferWithBackup(node->node, port_index, mem, &info->id);
if (ret != OK)
return OMX_ErrorUndefined;
OMX_BUFFERHEADERTYPE *buffer = (OMX_BUFFERHEADERTYPE*) calloc(1, sizeof(OMX_BUFFERHEADERTYPE));
*bufferptr = buffer;
buffer->pPlatformPrivate = info;
buffer->pAppPrivate = app_private;
buffer->nAllocLen = size;
buffer->pBuffer = (OMX_U8*) mem->pointer();
node->buffers.push_back(buffer);
return OMX_ErrorNone;
}
static OMX_ERRORTYPE iomx_free_buffer(OMX_HANDLETYPE component, OMX_U32 port, OMX_BUFFERHEADERTYPE *buffer)
{
OMXNode* node = (OMXNode*) ((OMX_COMPONENTTYPE*)component)->pComponentPrivate;
OMXBuffer* info = (OMXBuffer*) buffer->pPlatformPrivate;
status_t ret = ctx->iomx->freeBuffer(node->node, port, info->id);
for( List<OMX_BUFFERHEADERTYPE*>::iterator it = node->buffers.begin(); it != node->buffers.end(); it++ ) {
if (buffer == *it) {
node->buffers.erase(it);
break;
}
}
free(buffer);
delete info;
return get_error(ret);
}
static OMX_ERRORTYPE iomx_empty_this_buffer(OMX_HANDLETYPE component, OMX_BUFFERHEADERTYPE *buffer)
{
OMXNode* node = (OMXNode*) ((OMX_COMPONENTTYPE*)component)->pComponentPrivate;
OMXBuffer* info = (OMXBuffer*) buffer->pPlatformPrivate;
return get_error(ctx->iomx->emptyBuffer(node->node, info->id, buffer->nOffset, buffer->nFilledLen, buffer->nFlags, buffer->nTimeStamp));
}
static OMX_ERRORTYPE iomx_fill_this_buffer(OMX_HANDLETYPE component, OMX_BUFFERHEADERTYPE *buffer)
{
OMXNode* node = (OMXNode*) ((OMX_COMPONENTTYPE*)component)->pComponentPrivate;
OMXBuffer* info = (OMXBuffer*) buffer->pPlatformPrivate;
return get_error(ctx->iomx->fillBuffer(node->node, info->id));
}
static OMX_ERRORTYPE iomx_component_role_enum(OMX_HANDLETYPE component, OMX_U8 *role, OMX_U32 index)
{
OMXNode* node = (OMXNode*) ((OMX_COMPONENTTYPE*)component)->pComponentPrivate;
for( List<IOMX::ComponentInfo>::iterator it = ctx->components.begin(); it != ctx->components.end(); it++ ) {
if (node->component_name == it->mName) {
if (index >= it->mRoles.size())
return OMX_ErrorNoMore;
List<String8>::iterator it2 = it->mRoles.begin();
for( OMX_U32 i = 0; it2 != it->mRoles.end() && i < index; i++, it2++ ) ;
strncpy((char*)role, it2->string(), OMX_MAX_STRINGNAME_SIZE);
if (it2->length() >= OMX_MAX_STRINGNAME_SIZE)
role[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
return OMX_ErrorNone;
}
}
return OMX_ErrorInvalidComponentName;
}
static OMX_ERRORTYPE iomx_get_handle(OMX_HANDLETYPE *handle_ptr, const char *component_name, OMX_PTR app_data, const OMX_CALLBACKTYPE *callbacks)
{
OMXNode* node = new OMXNode();
node->app_data = app_data;
node->callbacks = *callbacks;
node->observer = new OMXCodecObserver();
node->observer->setNode(node);
node->state = OMX_StateLoaded;
node->component_name = component_name;
OMX_COMPONENTTYPE* component = (OMX_COMPONENTTYPE*) malloc(sizeof(OMX_COMPONENTTYPE));
memset(component, 0, sizeof(OMX_COMPONENTTYPE));
component->nSize = sizeof(OMX_COMPONENTTYPE);
component->nVersion.s.nVersionMajor = 1;
component->nVersion.s.nVersionMinor = 1;
component->nVersion.s.nRevision = 0;
component->nVersion.s.nStep = 0;
component->pComponentPrivate = node;
component->SendCommand = iomx_send_command;
component->GetParameter = iomx_get_parameter;
component->SetParameter = iomx_set_parameter;
component->FreeBuffer = iomx_free_buffer;
component->EmptyThisBuffer = iomx_empty_this_buffer;
component->FillThisBuffer = iomx_fill_this_buffer;
component->GetState = iomx_get_state;
component->AllocateBuffer = iomx_allocate_buffer;
component->ComponentRoleEnum = iomx_component_role_enum;
*handle_ptr = component;
node->handle = component;
status_t ret;
if ((ret = ctx->iomx->allocateNode( component_name, node->observer, &node->node )) != OK)
return OMX_ErrorUndefined;
return OMX_ErrorNone;
}
static OMX_ERRORTYPE iomx_free_handle(OMX_HANDLETYPE handle)
{
OMXNode* node = (OMXNode*) ((OMX_COMPONENTTYPE*)handle)->pComponentPrivate;
ctx->iomx->freeNode( node->node );
node->observer->setNode(NULL);
delete node;
free(handle);
return OMX_ErrorNone;
}
static OMX_ERRORTYPE iomx_init()
{
if (ctx->init_refcount > 0) {
ctx->init_refcount++;
return OMX_ErrorNone;
}
OMXClient client;
if (client.connect() != OK)
return OMX_ErrorUndefined;
ctx->iomx = client.interface();
ctx->init_refcount = 1;
ctx->iomx->listNodes(&ctx->components);
return OMX_ErrorNone;
}
static OMX_ERRORTYPE iomx_deinit()
{
ctx->init_refcount--;
if (ctx->init_refcount == 0) {
ctx->iomx = NULL;
}
return OMX_ErrorNone;
}
static OMX_ERRORTYPE iomx_component_name_enum(OMX_STRING component_name, OMX_U32 name_length, OMX_U32 index)
{
if (index >= ctx->components.size())
return OMX_ErrorNoMore;
List<IOMX::ComponentInfo>::iterator it = ctx->components.begin();
for( OMX_U32 i = 0; i < index; i++ )
it++;
strncpy(component_name, it->mName.string(), name_length);
component_name[name_length - 1] = '\0';
return OMX_ErrorNone;
}
static OMX_ERRORTYPE iomx_get_roles_of_component(OMX_STRING component_name, OMX_U32 *num_roles, OMX_U8 **roles)
{
for( List<IOMX::ComponentInfo>::iterator it = ctx->components.begin(); it != ctx->components.end(); it++ ) {
if (!strcmp(component_name, it->mName.string())) {
if (!roles) {
*num_roles = it->mRoles.size();
return OMX_ErrorNone;
}
if (*num_roles < it->mRoles.size())
return OMX_ErrorInsufficientResources;
*num_roles = it->mRoles.size();
OMX_U32 i = 0;
for( List<String8>::iterator it2 = it->mRoles.begin(); it2 != it->mRoles.end(); i++, it2++ ) {
strncpy((char*)roles[i], it2->string(), OMX_MAX_STRINGNAME_SIZE);
roles[i][OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
}
return OMX_ErrorNone;
}
}
return OMX_ErrorInvalidComponentName;
}
void* iomx_dlopen(const char *name)
{
if (!ctx)
ctx = new IOMXContext();
ctx->refcount++;
return ctx;
}
void iomx_dlclose(void *handle)
{
IOMXContext *ctx = (IOMXContext*) handle;
ctx->refcount--;
if (!ctx->refcount) {
delete ctx;
::ctx = NULL;
}
}
void *iomx_dlsym(void *handle, const char *name)
{
if (!strcmp(name, "OMX_Init"))
return (void*) iomx_init;
if (!strcmp(name, "OMX_Deinit"))
return (void*) iomx_deinit;
if (!strcmp(name, "OMX_GetHandle"))
return (void*) iomx_get_handle;
if (!strcmp(name, "OMX_FreeHandle"))
return (void*) iomx_free_handle;
if (!strcmp(name, "OMX_ComponentNameEnum"))
return (void*) iomx_component_name_enum;
if (!strcmp(name, "OMX_GetRolesOfComponent"))
return (void*) iomx_get_roles_of_component;
return NULL;
}
/*****************************************************************************
* iomx.h: Interface for the IOMX OpenMAX wrapper
*****************************************************************************
* Copyright (C) 2011 VLC authors and VideoLAN
*
* Authors: Martin Storsjo <martin@martin.st>
*
* 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 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.
*****************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
void* iomx_dlopen(const char *name);
void iomx_dlclose(void *handle);
void* iomx_dlsym(void *handle, const char *name);
#ifdef __cplusplus
}
#endif
......@@ -29,8 +29,15 @@
#endif
#include <dlfcn.h>
#if defined(USE_IOMX)
#include "iomx.h"
#define dll_open(name) iomx_dlopen(name)
#define dll_close(handle) iomx_dlclose(handle)
#define dlsym(handle, name) iomx_dlsym(handle, name)
#else
#define dll_open(name) dlopen( name, RTLD_NOW )
#define dll_close(handle) dlclose(handle)
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
......
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