]> git.sesse.net Git - vlc/commitdiff
omxil: Add an omxil based video output module
authorMartin Storsjö <martin@martin.st>
Mon, 11 Mar 2013 14:52:20 +0000 (16:52 +0200)
committerMartin Storsjö <martin@martin.st>
Mon, 18 Mar 2013 16:23:06 +0000 (18:23 +0200)
This is kept under modules/codes/omxil due to sharing much code
with the omxil codec.

This is only tested with the OMX.broadcom.video_render component
on the Raspberry Pi so far, which in itself doesn't advertise any
roles at all, so this could possibly use the "iv_renderer.yuv.overlay"
role instead of plan "iv_renderer".

But it should be possible to generalize this further and adapt it to
any OMX IL video renderer component.

Signed-off-by: Martin Storsjö <martin@martin.st>
configure.ac
modules/LIST
modules/codec/Modules.am
modules/codec/omxil/vout.c [new file with mode: 0644]
po/POTFILES.in

index 9fd748411cb419c5a2e419dfee35761120dcf24c..78e1faacef911cfbe87b7b398e57acc1b6e6954c 100644 (file)
@@ -2122,6 +2122,16 @@ then
   VLC_ADD_PLUGIN([omxil])
 fi
 
+dnl
+dnl openmax il vout plugin
+dnl
+AC_ARG_ENABLE(omxil-vout,
+  [  --enable-omxil-vout     openmax il video output module (default disabled)])
+if test "${enable_omxil_vout}" = "yes"
+then
+  VLC_ADD_PLUGIN([omxil_vout])
+fi
+
 dnl
 dnl CrystalHD codec plugin
 dnl
index 192f729d0edbbbb221340f056a5cd81eff88cf57..5071d8e79a521eed60521018f382f42a0fbd793e 100644 (file)
@@ -232,6 +232,7 @@ $Id$
  * ogg: input module for OGG decapsulation
  * oldrc: old interface module using stdio
  * omxil: OpenMAX IL audio/video decoder
+ * omxil_vout: OpenMAX IL video output
  * opencv_example: OpenCV example (face identification)
  * opencv_wrapper: OpenCV wrapper video filter
  * opensles_android: OpenSL ES audio output for Android
index bfd80232355b151b172ad61cc7f165db0111552c..a0929c36d7e1f07c6f2e39a2274f3fca0df2f4c4 100644 (file)
@@ -190,6 +190,10 @@ libomxil_plugin_la_SOURCES = \
 libomxil_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/omxil
 libomxil_plugin_la_LIBADD = $(AM_LIBADD) $(LIBDL)
 
+libomxil_vout_plugin_la_SOURCES = omxil/vout.c omxil/omxil_core.c omxil/utils.c omxil/qcom.c
+libomxil_vout_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/omxil
+libomxil_vout_plugin_la_LIBADD = $(AM_LIBADD) $(LIBDL)
+
 libiomx_plugin_la_SOURCES = $(libomxil_plugin_la_SOURCES)
 libiomx_plugin_la_CPPFLAGS = $(libomxil_plugin_la_CPPFLAGS) -DUSE_IOMX
 libiomx_plugin_la_LIBADD = $(libomxil_plugin_la_LIBADD)
@@ -197,8 +201,8 @@ libiomx_plugin_la_LIBADD = $(libomxil_plugin_la_LIBADD)
 libmediacodec_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/omxil
 libmediacodec_plugin_la_SOURCES = omxil/android_mediacodec.c omxil/utils.c
 
-libvlc_LTLIBRARIES += $(LTLIBomxil)
-EXTRA_LTLIBRARIES += libomxil_plugin.la
+libvlc_LTLIBRARIES += $(LTLIBomxil) $(LTLIBomxil_vout)
+EXTRA_LTLIBRARIES += libomxil_plugin.la libomxil_vout_plugin.la
 if HAVE_ANDROID
 libvlc_LTLIBRARIES += libiomx_plugin.la libmediacodec_plugin.la
 endif
diff --git a/modules/codec/omxil/vout.c b/modules/codec/omxil/vout.c
new file mode 100644 (file)
index 0000000..3389855
--- /dev/null
@@ -0,0 +1,423 @@
+/*****************************************************************************
+ * vout.c: OpenMAX IL video output
+ *****************************************************************************
+ * Copyright © 2013 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 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_vout_display.h>
+#include <vlc_picture_pool.h>
+
+#include "omxil.h"
+#include "omxil_core.h"
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int  Open (vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+    set_category(CAT_VIDEO)
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    set_shortname("omxil_vout")
+    set_description(N_("OpenMAX IL video output"))
+    set_capability("vout display", 0)
+    set_callbacks(Open, Close)
+vlc_module_end()
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+static picture_pool_t   *Pool  (vout_display_t *, unsigned);
+static void             Display(vout_display_t *, picture_t *, subpicture_t *);
+static int              Control(vout_display_t *, int, va_list);
+
+/* */
+struct vout_display_sys_t {
+    picture_pool_t *pool;
+
+    OMX_HANDLETYPE omx_handle;
+
+    char psz_component[OMX_MAX_STRINGNAME_SIZE];
+
+    OmxPort port;
+
+    OmxEventQueue event_queue;
+};
+
+struct picture_sys_t {
+    OMX_BUFFERHEADERTYPE *buf;
+    vout_display_sys_t *sys;
+};
+
+static int LockSurface(picture_t *);
+static void UnlockSurface(picture_t *);
+
+static OMX_ERRORTYPE OmxEventHandler(OMX_HANDLETYPE, OMX_PTR, OMX_EVENTTYPE,
+                                     OMX_U32, OMX_U32, OMX_PTR);
+static OMX_ERRORTYPE OmxEmptyBufferDone(OMX_HANDLETYPE, OMX_PTR,
+                                        OMX_BUFFERHEADERTYPE *);
+static OMX_ERRORTYPE OmxFillBufferDone(OMX_HANDLETYPE, OMX_PTR,
+                                       OMX_BUFFERHEADERTYPE *);
+
+static OMX_ERRORTYPE OmxEventHandler(OMX_HANDLETYPE omx_handle,
+    OMX_PTR app_data, OMX_EVENTTYPE event, OMX_U32 data_1,
+    OMX_U32 data_2, OMX_PTR event_data)
+{
+    vout_display_t *vd = (vout_display_t *)app_data;
+    vout_display_sys_t *p_sys = vd->sys;
+    (void)omx_handle;
+
+    PrintOmxEvent((vlc_object_t *) vd, event, data_1, data_2, event_data);
+    PostOmxEvent(&p_sys->event_queue, event, data_1, data_2, event_data);
+    return OMX_ErrorNone;
+}
+
+static OMX_ERRORTYPE OmxEmptyBufferDone(OMX_HANDLETYPE omx_handle,
+    OMX_PTR app_data, OMX_BUFFERHEADERTYPE *omx_header)
+{
+    vout_display_t *vd = (vout_display_t *)app_data;
+    vout_display_sys_t *p_sys = vd->sys;
+    (void)omx_handle;
+
+#ifndef NDEBUG
+    msg_Dbg(vd, "OmxEmptyBufferDone %p, %p", omx_header, omx_header->pBuffer);
+#endif
+
+    OMX_FIFO_PUT(&p_sys->port.fifo, omx_header);
+
+    return OMX_ErrorNone;
+}
+
+static OMX_ERRORTYPE OmxFillBufferDone(OMX_HANDLETYPE omx_handle,
+    OMX_PTR app_data, OMX_BUFFERHEADERTYPE *omx_header)
+{
+    (void)omx_handle;
+    (void)app_data;
+    (void)omx_header;
+
+    return OMX_ErrorNone;
+}
+
+static int Open(vlc_object_t *p_this)
+{
+    vout_display_t *vd = (vout_display_t *)p_this;
+    vout_display_t *p_dec = vd;
+    char ppsz_components[MAX_COMPONENTS_LIST_SIZE][OMX_MAX_STRINGNAME_SIZE];
+    picture_t** pictures = NULL;
+
+    static OMX_CALLBACKTYPE callbacks =
+        { OmxEventHandler, OmxEmptyBufferDone, OmxFillBufferDone };
+
+    if (InitOmxCore(p_this) != VLC_SUCCESS)
+        return VLC_EGENERIC;
+
+    int components = CreateComponentsList(p_this, "iv_renderer", ppsz_components);
+    if (components <= 0) {
+        DeinitOmxCore();
+        return VLC_EGENERIC;
+    }
+
+    /* Allocate structure */
+    vout_display_sys_t *p_sys = (struct vout_display_sys_t*) calloc(1, sizeof(*p_sys));
+    if (!p_sys) {
+        DeinitOmxCore();
+        return VLC_ENOMEM;
+    }
+
+    vd->sys     = p_sys;
+    strcpy(p_sys->psz_component, ppsz_components[0]);
+
+    /* Load component */
+    OMX_ERRORTYPE omx_error = pf_get_handle(&p_sys->omx_handle,
+                                            p_sys->psz_component, vd, &callbacks);
+    CHECK_ERROR(omx_error, "OMX_GetHandle(%s) failed (%x: %s)",
+                p_sys->psz_component, omx_error, ErrorToString(omx_error));
+
+    InitOmxEventQueue(&p_sys->event_queue);
+    vlc_mutex_init (&p_sys->port.fifo.lock);
+    vlc_cond_init (&p_sys->port.fifo.wait);
+    p_sys->port.fifo.offset = offsetof(OMX_BUFFERHEADERTYPE, pOutputPortPrivate) / sizeof(void *);
+    p_sys->port.fifo.pp_last = &p_sys->port.fifo.p_first;
+    p_sys->port.b_direct = false;
+    p_sys->port.b_flushed = true;
+
+    OMX_PORT_PARAM_TYPE param;
+    OMX_INIT_STRUCTURE(param);
+    omx_error = OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamVideoInit, &param);
+    CHECK_ERROR(omx_error, "OMX_GetParameter(OMX_IndexParamVideoInit) failed (%x: %s)",
+                omx_error, ErrorToString(omx_error));
+
+    p_sys->port.i_port_index = param.nStartPortNumber;
+    p_sys->port.b_valid = true;
+    p_sys->port.omx_handle = p_sys->omx_handle;
+
+    OMX_INIT_STRUCTURE(p_sys->port.definition);
+    p_sys->port.definition.nPortIndex = p_sys->port.i_port_index;
+    omx_error = OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, &p_sys->port.definition);
+    CHECK_ERROR(omx_error, "OMX_GetParameter(OMX_IndexParamPortDefinition) failed (%x: %s)",
+                omx_error, ErrorToString(omx_error));
+
+#define ALIGN_16_PIXELS(x) (((x) + 15) / 16 * 16)
+
+    p_sys->port.definition.format.video.nFrameWidth = ALIGN_16_PIXELS(vd->cfg->display.width);
+    p_sys->port.definition.format.video.nFrameHeight = ALIGN_16_PIXELS(vd->cfg->display.height);
+    p_sys->port.definition.format.video.nStride = p_sys->port.definition.format.video.nFrameWidth;
+    p_sys->port.definition.format.video.nSliceHeight = p_sys->port.definition.format.video.nFrameHeight;
+    p_sys->port.definition.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
+
+    omx_error = OMX_SetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, &p_sys->port.definition);
+    CHECK_ERROR(omx_error, "OMX_SetParameter(OMX_IndexParamPortDefinition) failed (%x: %s)",
+                omx_error, ErrorToString(omx_error));
+    OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, &p_sys->port.definition);
+
+    p_sys->port.pp_buffers =
+            malloc(p_sys->port.definition.nBufferCountActual *
+                   sizeof(OMX_BUFFERHEADERTYPE*));
+    p_sys->port.i_buffers = p_sys->port.definition.nBufferCountActual;
+
+    omx_error = OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateIdle, 0);
+    CHECK_ERROR(omx_error, "OMX_CommandStateSet Idle failed (%x: %s)",
+                omx_error, ErrorToString(omx_error));
+
+    unsigned int i;
+    for (i = 0; i < p_sys->port.i_buffers; i++) {
+        omx_error = OMX_AllocateBuffer(p_sys->omx_handle, &p_sys->port.pp_buffers[i],
+                                       p_sys->port.i_port_index, 0,
+                                       p_sys->port.definition.nBufferSize);
+        if (omx_error != OMX_ErrorNone)
+            break;
+        OMX_FIFO_PUT(&p_sys->port.fifo, p_sys->port.pp_buffers[i]);
+    }
+    if (omx_error != OMX_ErrorNone) {
+        p_sys->port.i_buffers = i;
+        for (i = 0; i < p_sys->port.i_buffers; i++)
+            OMX_FreeBuffer(p_sys->omx_handle, p_sys->port.i_port_index, p_sys->port.pp_buffers[i]);
+        msg_Err(vd, "OMX_AllocateBuffer failed (%x: %s)",
+                omx_error, ErrorToString(omx_error));
+        goto error;
+    }
+
+    omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
+    CHECK_ERROR(omx_error, "Wait for Idle failed (%x: %s)",
+                omx_error, ErrorToString(omx_error));
+
+    omx_error = OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet,
+                                OMX_StateExecuting, 0);
+    CHECK_ERROR(omx_error, "OMX_CommandStateSet Executing failed (%x: %s)",
+                omx_error, ErrorToString(omx_error));
+    omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
+    CHECK_ERROR(omx_error, "Wait for Executing failed (%x: %s)",
+                omx_error, ErrorToString(omx_error));
+
+
+    /* Setup chroma */
+    video_format_t fmt = vd->fmt;
+
+    fmt.i_chroma = VLC_CODEC_I420;
+    video_format_FixRgb(&fmt);
+
+    /* Setup vout_display */
+    vd->fmt     = fmt;
+    vd->pool    = Pool;
+    vd->display = Display;
+    vd->control = Control;
+    vd->prepare = NULL;
+    vd->manage  = NULL;
+
+    /* Create the associated picture */
+    pictures = calloc(sizeof(*pictures), p_sys->port.i_buffers);
+    if (!pictures)
+        goto error;
+    for (unsigned int i = 0; i < p_sys->port.i_buffers; i++) {
+        picture_resource_t resource = { 0 };
+        picture_resource_t *rsc = &resource;
+        rsc->p_sys = malloc(sizeof(*rsc->p_sys));
+        if (!rsc->p_sys)
+            goto error;
+        rsc->p_sys->sys = p_sys;
+
+        for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
+            rsc->p[i].p_pixels = NULL;
+            rsc->p[i].i_pitch = 0;
+            rsc->p[i].i_lines = 0;
+        }
+        picture_t *picture = picture_NewFromResource(&fmt, rsc);
+        if (!picture)
+            goto error;
+        pictures[i] = picture;
+    }
+
+    /* Wrap it into a picture pool */
+    picture_pool_configuration_t pool_cfg;
+    memset(&pool_cfg, 0, sizeof(pool_cfg));
+    pool_cfg.picture_count = p_sys->port.i_buffers;
+    pool_cfg.picture       = pictures;
+    pool_cfg.lock          = LockSurface;
+    pool_cfg.unlock        = UnlockSurface;
+
+    p_sys->pool = picture_pool_NewExtended(&pool_cfg);
+    if (!p_sys->pool) {
+        for (unsigned int i = 0; i < p_sys->port.i_buffers; i++)
+            picture_Release(pictures[i]);
+        goto error;
+    }
+
+    /* Fix initial state */
+    vout_display_SendEventFullscreen(vd, true);
+
+    free(pictures);
+    return VLC_SUCCESS;
+
+error:
+    free(pictures);
+    Close(p_this);
+    return VLC_EGENERIC;
+}
+
+static void Close(vlc_object_t *p_this)
+{
+    vout_display_t *vd = (vout_display_t *)p_this;
+    vout_display_sys_t *p_sys = vd->sys;
+
+    if (p_sys->omx_handle) {
+        OMX_STATETYPE state;
+        OMX_GetState(p_sys->omx_handle, &state);
+        if (state == OMX_StateExecuting) {
+            OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateIdle, 0);
+            while (1) {
+                OMX_U32 cmd, state;
+                WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, &cmd, &state, 0);
+                if (cmd == OMX_CommandStateSet && state == OMX_StateIdle)
+                    break;
+            }
+        }
+        OMX_GetState(p_sys->omx_handle, &state);
+        if (state == OMX_StateIdle) {
+            OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateLoaded, 0);
+            for (unsigned int i = 0; i < p_sys->port.i_buffers; i++) {
+                OMX_BUFFERHEADERTYPE *p_buffer;
+                OMX_FIFO_GET(&p_sys->port.fifo, p_buffer);
+                OMX_FreeBuffer(p_sys->omx_handle, p_sys->port.i_port_index, p_buffer);
+            }
+            WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
+        }
+        free(p_sys->port.pp_buffers);
+        pf_free_handle(p_sys->omx_handle);
+        DeinitOmxEventQueue(&p_sys->event_queue);
+        vlc_mutex_destroy(&p_sys->port.fifo.lock);
+        vlc_cond_destroy(&p_sys->port.fifo.wait);
+    }
+
+    if (p_sys->pool)
+        picture_pool_Delete(p_sys->pool);
+    free(p_sys);
+    DeinitOmxCore();
+}
+
+static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
+{
+    VLC_UNUSED(count);
+
+    return vd->sys->pool;
+}
+
+static int LockSurface(picture_t *picture)
+{
+    picture_sys_t *picsys = picture->p_sys;
+    vout_display_sys_t *p_sys = picsys->sys;
+    OMX_BUFFERHEADERTYPE *p_buffer;
+
+    OMX_FIFO_GET(&p_sys->port.fifo, p_buffer);
+    for (int i = 0; i < 3; i++) {
+        picture->p[i].p_pixels = p_buffer->pBuffer;
+        picture->p[i].i_pitch = p_sys->port.definition.format.video.nStride;
+        picture->p[i].i_lines = p_sys->port.definition.format.video.nSliceHeight;
+        if (i > 0) {
+            picture->p[i].p_pixels = picture->p[i-1].p_pixels + picture->p[i-1].i_pitch*picture->p[i-1].i_lines;
+            picture->p[i].i_pitch /= 2;
+            picture->p[i].i_lines /= 2;
+        }
+    }
+    p_buffer->nOffset = 0;
+    p_buffer->nFlags = 0;
+    p_buffer->nTimeStamp = ToOmxTicks(0);
+    p_buffer->nFilledLen = 0;
+    picsys->buf = p_buffer;
+
+    return VLC_SUCCESS;
+}
+
+static void UnlockSurface(picture_t *picture)
+{
+    picture_sys_t *picsys = picture->p_sys;
+    vout_display_sys_t *p_sys = picsys->sys;
+    OMX_BUFFERHEADERTYPE *p_buffer = picsys->buf;
+
+    if (!p_buffer->nFilledLen)
+        OMX_FIFO_PUT(&p_sys->port.fifo, p_buffer);
+    else
+        OMX_EmptyThisBuffer(p_sys->omx_handle, p_buffer);
+}
+
+static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
+{
+    VLC_UNUSED(vd);
+    VLC_UNUSED(subpicture);
+    picture_sys_t *picsys = picture->p_sys;
+    vout_display_sys_t *p_sys = picsys->sys;
+    OMX_BUFFERHEADERTYPE *p_buffer = picsys->buf;
+
+    p_buffer->nFilledLen = 3*p_sys->port.definition.format.video.nStride*p_sys->port.definition.format.video.nSliceHeight/2;
+    p_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
+
+    /* refcount lowers to 0, and pool_cfg.unlock is called */
+
+    picture_Release(picture);
+}
+
+static int Control(vout_display_t *vd, int query, va_list args)
+{
+    VLC_UNUSED(args);
+
+    switch (query) {
+    case VOUT_DISPLAY_HIDE_MOUSE:
+        return VLC_SUCCESS;
+
+    default:
+        msg_Err(vd, "Unknown request in omxil vout display");
+
+    case VOUT_DISPLAY_CHANGE_FULLSCREEN:
+    case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
+    case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
+    case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
+    case VOUT_DISPLAY_CHANGE_ZOOM:
+    case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
+    case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
+    case VOUT_DISPLAY_GET_OPENGL:
+        return VLC_EGENERIC;
+    }
+}
index 8f357ed6643945f191039c740b07f9bafad4dc7d..161a0c4aa221ef0985c9c000ccec43e127698c59 100644 (file)
@@ -381,6 +381,7 @@ modules/codec/omxil/omxil.c
 modules/codec/omxil/omxil.h
 modules/codec/omxil/omxil_utils.h
 modules/codec/omxil/utils.c
+modules/codec/omxil/vout.c
 modules/codec/opus.c
 modules/codec/png.c
 modules/codec/quicktime.c