[vlc-devel] [RFC] [PATCH 2/2] omxil: Allow using IOMX on Android

Martin Storsjö martin at martin.st
Wed Sep 21 14:49:33 CEST 2011


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 enabled in production on whitelisted device/firmware
combinations that are known to work.

On newer qualcomm builds, the IOMX headers from the Android
source tree must be patched with
http://albin.abo.fi/~mstorsjo/0001-Add-interface-changes-from-Stagefright-Memcpy-optimi.patch
which is a simplified version of the header parts of
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=commit;h=052368f194c9fc180b9b0335b60114a2f1fb88d8

The separate omxil-iomx.c file is an ugly hack, but IIRC
the reason was that I had problems getting a special define
set for these source files when built as iomx.

The configure.ac change lacks a check for whether the necessary
headers are available.
---
 configure.ac                     |   12 ++
 modules/codec/omxil/Modules.am   |    4 +
 modules/codec/omxil/iomx.cpp     |  394 ++++++++++++++++++++++++++++++++++++++
 modules/codec/omxil/iomx.h       |   33 ++++
 modules/codec/omxil/omxil-iomx.c |    2 +
 modules/codec/omxil/omxil.c      |    7 +
 6 files changed, 452 insertions(+), 0 deletions(-)
 create mode 100644 modules/codec/omxil/iomx.cpp
 create mode 100644 modules/codec/omxil/iomx.h
 create mode 100644 modules/codec/omxil/omxil-iomx.c

diff --git a/configure.ac b/configure.ac
index 854753d..d27174c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2323,6 +2323,18 @@ then
 fi
 
 dnl
+dnl iomx codec plugin
+dnl
+AC_ARG_ENABLE(iomx,
+  [  --enable-iomx           iomx codec module (default disabled)])
+if test "${enable_iomx}" = "yes"
+then
+  VLC_ADD_PLUGIN([iomx])
+  VLC_ADD_CXXFLAGS([iomx],[-fno-exceptions -fno-rtti])
+  VLC_ADD_LIBS([iomx], [-lstagefright -lmedia -lutils -lbinder])
+fi
+
+dnl
 dnl CrystalHD codec plugin
 dnl
 AC_ARG_ENABLE(crystalhd,
diff --git a/modules/codec/omxil/Modules.am b/modules/codec/omxil/Modules.am
index b2d447d..6ee6284 100644
--- a/modules/codec/omxil/Modules.am
+++ b/modules/codec/omxil/Modules.am
@@ -1,3 +1,7 @@
 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
+
+SOURCES_iomx = omxil-iomx.c utils.c iomx.cpp 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
diff --git a/modules/codec/omxil/iomx.cpp b/modules/codec/omxil/iomx.cpp
new file mode 100644
index 0000000..1f2346e
--- /dev/null
+++ b/modules/codec/omxil/iomx.cpp
@@ -0,0 +1,394 @@
+/*****************************************************************************
+ * iomx.cpp: OpenMAX interface implementation based on IOMX
+ *****************************************************************************
+ * Copyright (C) 2011 the VideoLAN team
+ *
+ * Authors: Martin Storsjo <martin at martin.st>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 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 <vlc_plugin.h>
+#include <vlc_codec.h>
+#include <vlc_aout.h>
+#include <vlc_block_helper.h>
+#include <vlc_cpu.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;
+}
+
diff --git a/modules/codec/omxil/iomx.h b/modules/codec/omxil/iomx.h
new file mode 100644
index 0000000..e0a3f05
--- /dev/null
+++ b/modules/codec/omxil/iomx.h
@@ -0,0 +1,33 @@
+/*****************************************************************************
+ * iomx.h: Interface for the IOMX OpenMAX wrapper
+ *****************************************************************************
+ * Copyright (C) 2011 the VideoLAN team
+ *
+ * Authors: Martin Storsjo <martin at martin.st>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 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
diff --git a/modules/codec/omxil/omxil-iomx.c b/modules/codec/omxil/omxil-iomx.c
new file mode 100644
index 0000000..7582a6a
--- /dev/null
+++ b/modules/codec/omxil/omxil-iomx.c
@@ -0,0 +1,2 @@
+#define USE_IOMX
+#include "omxil.c"
diff --git a/modules/codec/omxil/omxil.c b/modules/codec/omxil/omxil.c
index f7f0182..a1a9bb6 100644
--- a/modules/codec/omxil/omxil.c
+++ b/modules/codec/omxil/omxil.c
@@ -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>
-- 
1.7.2.5




More information about the vlc-devel mailing list