From ae1f6ea602a5b206f61e678b5249c01abe7cb7df Mon Sep 17 00:00:00 2001 From: Jonathan Sieber Date: Thu, 6 Apr 2017 18:36:58 +0200 Subject: Video Display Support for Raspberry Pi (#228) * Adding a new video display module for the Raspberry Pi... * Made it work with libomxil-bellagio - Fixed framing woes with vidconv() :) * Some Attention to the build system, Automatically use libomx-bellagio or RPi OMX API Introduces USE_OMX_RPI and USE_OMX_BELLAGIO in mk/modules.mk use sys_usleep() Removed pthread mutexes, they are not needed anymore --- modules/omx/omx.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 modules/omx/omx.c (limited to 'modules/omx/omx.c') diff --git a/modules/omx/omx.c b/modules/omx/omx.c new file mode 100644 index 0000000..8f1c069 --- /dev/null +++ b/modules/omx/omx.c @@ -0,0 +1,326 @@ +/** + * @file omx.c Raspberry Pi VideoCoreIV OpenMAX interface + * + * Copyright (C) 2016 - 2017 Creytiv.com + * Copyright (C) 2016 - 2017 Jonathan Sieber + */ + +#include "omx.h" + +#include +#include +#include + +#include +#include +#include + +/* Avoids a VideoCore header warning about clock_gettime() */ +#include +#include + +/** + * @defgroup omx omx + * + * TODO: + * * Proper sync OMX events across threads, instead of busy waiting + */ + +#ifdef RASPBERRY_PI +static const int VIDEO_RENDER_PORT = 90; +#else +static const int VIDEO_RENDER_PORT = 0; +#endif + +/* +static void setHeader(OMX_PTR header, OMX_U32 size) { + OMX_VERSIONTYPE* ver = (OMX_VERSIONTYPE*)(header + sizeof(OMX_U32)); + *((OMX_U32*)header) = size; + + ver->s.nVersionMajor = VERSIONMAJOR; + ver->s.nVersionMinor = VERSIONMINOR; + ver->s.nRevision = VERSIONREVISION; + ver->s.nStep = VERSIONSTEP; +} +* */ + +static OMX_ERRORTYPE EventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, + OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, + OMX_PTR pEventData) +{ + (void) hComponent; + switch (eEvent) { + case OMX_EventCmdComplete: + debug("omx.EventHandler: Previous command completed\n" + "d1=%x\td2=%x\teventData=%p\tappdata=%p\n", + nData1, nData2, pEventData, pAppData); + /* TODO: Put these event into a multithreaded queue, + * properly wait for them in the issuing code */ + break; + case OMX_EventError: + warning("omx.EventHandler: Error event type " + "data1=%x\tdata2=%x\n", nData1, nData2); + break; + default: + warning("omx.EventHandler: Unknown event type %d\t" + "data1=%x data2=%x\n", eEvent, nData1, nData2); + return -1; + break; + } + return 0; +} + +static OMX_ERRORTYPE EmptyBufferDone( + OMX_HANDLETYPE hComponent, + OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer) +{ + (void) hComponent; + (void) pAppData; + (void) pBuffer; + + /* TODO: Wrap every call that can generate an event, + * and panic if an unexpected event arrives */ + return 0; +} + +static OMX_ERRORTYPE FillBufferDone(OMX_HANDLETYPE hComponent, + OMX_PTR pAppData, OMX_BUFFERHEADERTYPE* pBuffer) +{ + (void) hComponent; + (void) pAppData; + (void) pBuffer; + debug("FillBufferDone\n"); + return 0; +} + +static struct OMX_CALLBACKTYPE callbacks = { + EventHandler, + EmptyBufferDone, + &FillBufferDone +}; + +int omx_init(struct omx_state* st) +{ + OMX_ERRORTYPE err; +#ifdef RASPBERRY_PI + bcm_host_init(); +#endif + + st->buffers = NULL; + + err = OMX_Init(); +#ifdef RASPBERRY_PI + err |= OMX_GetHandle(&st->video_render, + "OMX.broadcom.video_render", 0, &callbacks); +#else + err |= OMX_GetHandle(&st->video_render, + "OMX.st.video.xvideosink", 0, &callbacks); +#endif + + if (!st->video_render || err != OMX_ERROR_NONE) { + error("Failed to create OMX video_render component"); + return ENOENT; + } + else { + info("created video_render component"); + return 0; + } +} + + +/* Some busy loops to verify we're running in order */ +static void block_until_state_changed(OMX_HANDLETYPE hComponent, + OMX_STATETYPE wanted_eState) +{ + OMX_STATETYPE eState; + unsigned int i = 0; + while (i++ == 0 || eState != wanted_eState) { + OMX_GetState(hComponent, &eState); + if (eState != wanted_eState) { + sys_usleep(10000); + } + } +} + + +void omx_deinit(struct omx_state* st) +{ + info("omx_deinit"); + OMX_SendCommand(st->video_render, + OMX_CommandStateSet, OMX_StateIdle, NULL); + block_until_state_changed(st->video_render, OMX_StateIdle); + OMX_SendCommand(st->video_render, + OMX_CommandStateSet, OMX_StateLoaded, NULL); + block_until_state_changed(st->video_render, OMX_StateLoaded); + OMX_FreeHandle(st->video_render); + OMX_Deinit(); +} + +void omx_display_disable(struct omx_state* st) +{ + (void)st; + + #ifdef RASPBERRY_PI + OMX_ERRORTYPE err; + OMX_CONFIG_DISPLAYREGIONTYPE config; + memset(&config, 0, sizeof(OMX_CONFIG_DISPLAYREGIONTYPE)); + config.nSize = sizeof(OMX_CONFIG_DISPLAYREGIONTYPE); + config.nVersion.nVersion = OMX_VERSION; + config.nPortIndex = VIDEO_RENDER_PORT; + config.fullscreen = 0; + config.set = OMX_DISPLAY_SET_FULLSCREEN; + + err = OMX_SetParameter(st->video_render, + OMX_IndexConfigDisplayRegion, &config); + + if (err != OMX_ERROR_NONE) { + warning("omx_display_disable command failed"); + } + + #endif +} + +static void block_until_port_changed(OMX_HANDLETYPE hComponent, + OMX_U32 nPortIndex, OMX_BOOL bEnabled) { + + OMX_ERRORTYPE r; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + OMX_U32 i = 0; + + memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = nPortIndex; + + while (i++ == 0 || portdef.bEnabled != bEnabled) { + r = OMX_GetParameter(hComponent, + OMX_IndexParamPortDefinition, &portdef); + if (r != OMX_ErrorNone) { + error("block_until_port_changed: OMX_GetParameter " + " failed with Result=%d\n", r); + } + if (portdef.bEnabled != bEnabled) { + sys_usleep(10000); + } + } +} + +int omx_display_enable(struct omx_state* st, + int width, int height, int stride) +{ + unsigned int i; + OMX_PARAM_PORTDEFINITIONTYPE portdef; +#ifdef RASPBERRY_PI + OMX_CONFIG_DISPLAYREGIONTYPE config; +#endif + OMX_ERRORTYPE err = OMX_ERROR_NONE; + + info("omx_update_size %d %d\n", width, height); + + #ifdef RASPBERRY_PI + memset(&config, 0, sizeof(OMX_CONFIG_DISPLAYREGIONTYPE)); + config.nSize = sizeof(OMX_CONFIG_DISPLAYREGIONTYPE); + config.nVersion.nVersion = OMX_VERSION; + config.nPortIndex = VIDEO_RENDER_PORT; + config.fullscreen = 1; + config.set = OMX_DISPLAY_SET_FULLSCREEN; + + err |= OMX_SetParameter(st->video_render, + OMX_IndexConfigDisplayRegion, &config); + + #endif + + memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = VIDEO_RENDER_PORT; + + /* specify buffer requirements */ + err |= OMX_GetParameter(st->video_render, + OMX_IndexParamPortDefinition, &portdef); + + portdef.format.video.nFrameWidth = width; + portdef.format.video.nFrameHeight = height; + portdef.format.video.nStride = stride; + portdef.format.video.nSliceHeight = height; + portdef.bEnabled = 1; + + if (err != OMX_ERROR_NONE) { + error("omx_display_enable: failed to set up video port"); + err = ENOMEM; + goto exit; + } + + + err |= OMX_SetParameter(st->video_render, + OMX_IndexParamPortDefinition, &portdef); + block_until_port_changed(st->video_render, VIDEO_RENDER_PORT, true); + + err |= OMX_GetParameter(st->video_render, + OMX_IndexParamPortDefinition, &portdef); + + if (err != OMX_ERROR_NONE || !portdef.bEnabled) { + error("omx_display_enable: failed to set up video port"); + err = ENOMEM; + goto exit; + } + + /* HACK: This state-change sometimes hangs for unknown reasons, + * so we just send the state command and wait 50 ms */ + /* block_until_state_changed(st->video_render, OMX_StateIdle); */ + + OMX_SendCommand(st->video_render, OMX_CommandStateSet, + OMX_StateIdle, NULL); + sys_usleep(50000); + + if (!st->buffers) { + st->buffers = + malloc(portdef.nBufferCountActual * sizeof(void*)); + st->num_buffers = portdef.nBufferCountActual; + st->current_buffer = 0; + + for (i = 0; i < portdef.nBufferCountActual; i++) { + err = OMX_AllocateBuffer(st->video_render, + &st->buffers[i], VIDEO_RENDER_PORT, + st, portdef.nBufferSize); + if (err) { + error("OMX_AllocateBuffer failed: %d\n", err); + err = ENOMEM; + goto exit; + } + } + } + + debug("omx_update_size: send to execute state"); + OMX_SendCommand(st->video_render, OMX_CommandStateSet, + OMX_StateExecuting, NULL); + block_until_state_changed(st->video_render, OMX_StateExecuting); + +exit: + return err; +} + + +int omx_display_input_buffer(struct omx_state* st, + void** pbuf, uint32_t* plen) +{ + if (!st->buffers) return EINVAL; + + *pbuf = st->buffers[0]->pBuffer; + *plen = st->buffers[0]->nAllocLen; + + st->buffers[0]->nFilledLen = *plen; + st->buffers[0]->nOffset = 0; + + return 0; +} + +int omx_display_flush_buffer(struct omx_state* st) +{ + if (OMX_EmptyThisBuffer(st->video_render, st->buffers[0]) + != OMX_ErrorNone) { + error("OMX_EmptyThisBuffer error"); + } + + return 0; +} -- cgit v1.2.3