diff --git a/.gitignore b/.gitignore index c489218..520e309 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /vgcore.* -/src/*.o /ustreamer +*.o diff --git a/src/omx/component.c b/src/omx/component.c new file mode 100644 index 0000000..312d102 --- /dev/null +++ b/src/omx/component.c @@ -0,0 +1,123 @@ +#include + +#include +#include + +#include "../logging.h" + +#include "formatters.h" +#include "component.h" + + +static int _component_wait_port_changed(OMX_HANDLETYPE *component, const OMX_U32 port, const OMX_BOOL enabled); +static int _component_wait_state_changed(OMX_HANDLETYPE *component, const OMX_STATETYPE wanted); + + +int component_enable_port(OMX_HANDLETYPE *component, const OMX_U32 port) { + OMX_ERRORTYPE error; + + LOG_DEBUG("Enabling OMX port %u ...", port); + if ((error = OMX_SendCommand(*component, OMX_CommandPortEnable, port, NULL)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't enable OMX port %u", port); + return -1; + } + return _component_wait_port_changed(component, port, OMX_TRUE); +} + +int component_disable_port(OMX_HANDLETYPE *component, const OMX_U32 port) { + OMX_ERRORTYPE error; + + LOG_DEBUG("Disabling OMX port %u ...", port); + if ((error = OMX_SendCommand(*component, OMX_CommandPortDisable, port, NULL)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't disable OMX port %u", port); + return -1; + } + return _component_wait_port_changed(component, port, OMX_FALSE); +} + +int component_get_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYPE *portdef, const OMX_U32 port) { + OMX_ERRORTYPE error; + + OMX_INIT_STRUCTURE(*portdef); + portdef->nPortIndex = port; + + LOG_DEBUG("Fetching OMX port %u definition ...", port); + if ((error = OMX_GetParameter(*component, OMX_IndexParamPortDefinition, portdef)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't get OMX port %u definition", port); + return -1; + } + return 0; +} + +int component_set_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYPE *portdef) { + OMX_ERRORTYPE error; + + LOG_DEBUG("Writing OMX port %u definition ...", port); + if ((error = OMX_SetParameter(*component, OMX_IndexParamPortDefinition, portdef)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't set OMX port %u definition", portdef->nPortIndex); + return -1; + } + return 0; +} + +int component_set_state(OMX_HANDLETYPE *component, const OMX_STATETYPE state) { + OMX_ERRORTYPE error; + + LOG_DEBUG("Switching component state to %s ...", omx_state_to_string(state)); + if ((error = OMX_SendCommand(*component, OMX_CommandStateSet, state, NULL)) != OMX_ErrorNone) { + //if (error == OMX_ErrorSameState) { + // return 0; + //} + LOG_OMX_ERROR(error, "Can't switch OMX component state to %s", omx_state_to_string(state)); + return -1; + } + return _component_wait_state_changed(component, state); +} + + +static int _component_wait_port_changed(OMX_HANDLETYPE *component, const OMX_U32 port, const OMX_BOOL enabled) { + OMX_ERRORTYPE error; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + int retries = 50; + + OMX_INIT_STRUCTURE(portdef); + portdef.nPortIndex = port; + + do { + if ((error = OMX_GetParameter(*component, OMX_IndexParamPortDefinition, &portdef)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't get OMX port %u definition for waiting", port); + return -1; + } + + if (portdef.bEnabled != enabled) { + LOG_DEBUG("Waiting for OMX %s port %u", (enabled ? "enabling" : "disabling"), port); + retries -= 1; + usleep(8000); + } + } while (portdef.bEnabled != enabled && retries); + + LOG_DEBUG("OMX port %u %s", port, (enabled ? "enabled" : "disabled")); + return (portdef.bEnabled == enabled ? 0 : -1); +} + +static int _component_wait_state_changed(OMX_HANDLETYPE *component, const OMX_STATETYPE wanted) { + OMX_ERRORTYPE error; + OMX_STATETYPE state; + int retries = 50; + + do { + if ((error = OMX_GetState(*component, &state)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Failed to get OMX component state"); + return -1; + } + + if (state != wanted) { + LOG_DEBUG("Waiting when OMX component state changes to %s", omx_state_to_string(wanted)); + retries -= 1; + usleep(8000); + } + } while (state != wanted && retries); + + LOG_DEBUG("Switched OMX component state to %s", omx_state_to_string(wanted)) + return (state == wanted ? 0 : -1); +} diff --git a/src/omx/component.h b/src/omx/component.h new file mode 100644 index 0000000..d628878 --- /dev/null +++ b/src/omx/component.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include +#include + + +#define OMX_INIT_STRUCTURE(_var) { \ + memset(&(_var), 0, sizeof(_var)); \ + (_var).nSize = sizeof(_var); \ + (_var).nVersion.nVersion = OMX_VERSION; \ + (_var).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \ + (_var).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \ + (_var).nVersion.s.nRevision = OMX_VERSION_REVISION; \ + (_var).nVersion.s.nStep = OMX_VERSION_STEP; \ + } + + +int component_enable_port(OMX_HANDLETYPE *component, const OMX_U32 port); +int component_disable_port(OMX_HANDLETYPE *component, const OMX_U32 port); +int component_get_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYPE *portdef, const OMX_U32 port); +int component_set_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYPE *portdef); +int component_set_state(OMX_HANDLETYPE *component, const OMX_STATETYPE state); diff --git a/src/omx/encoder.c b/src/omx/encoder.c new file mode 100644 index 0000000..2cc2e1f --- /dev/null +++ b/src/omx/encoder.c @@ -0,0 +1,405 @@ +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "../logging.h" +#include "../tools.h" +#include "../device.h" + +#include "formatters.h" +#include "component.h" +#include "encoder.h" + + +#define INPUT_PORT 340 +#define OUTPUT_PORT 341 + + +static int _omx_init_component(struct omx_encoder_t *omx); +static int _omx_init_disable_ports(struct omx_encoder_t *omx); +static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev); +static int _omx_setup_output(struct omx_encoder_t *omx, struct device_t *dev); +static int _omx_encoder_clear_ports(struct omx_encoder_t *omx); + +static OMX_ERRORTYPE _omx_event_handler(UNUSED OMX_HANDLETYPE encoder, + OMX_PTR v_omx, OMX_EVENTTYPE event, OMX_U32 data1, + UNUSED OMX_U32 data2, UNUSED OMX_PTR event_data); + +static OMX_ERRORTYPE _omx_input_required_handler(UNUSED OMX_HANDLETYPE encoder, + OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buffer); + +static OMX_ERRORTYPE _omx_output_available_handler(UNUSED OMX_HANDLETYPE encoder, + OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buffer); + + +struct omx_encoder_t *omx_encoder_init() { + // Some theory: + // - http://www.fourcc.org/yuv.php + // - https://kwasi-ich.de/blog/2017/11/26/omx/ + // - https://github.com/hopkinskong/rpi-omx-jpeg-encode/blob/master/jpeg_bench.cpp + // - https://github.com/kwasmich/OMXPlayground/blob/master/omxJPEGEnc.c + // - https://github.com/gagle/raspberrypi-openmax-jpeg/blob/master/jpeg.c + // - https://www.raspberrypi.org/forums/viewtopic.php?t=154790 + // - https://bitbucket.org/bensch128/omxjpegencode/src/master/jpeg_encoder.cpp + + struct omx_encoder_t *omx; + OMX_ERRORTYPE error; + + A_CALLOC(omx, 1); + + LOG_INFO("Initializing OMX JPEG encoder ..."); + + LOG_DEBUG("Initializing BCM ..."); + bcm_host_init(); + + LOG_DEBUG("Initializing OMX ..."); + if ((error = OMX_Init()) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't initialize OMX"); + goto error; + } + omx->i_omx = true; + + if (vcos_semaphore_create(&omx->handler_lock, "handler_lock", 0) != VCOS_SUCCESS) { + LOG_ERROR("Can't create VCOS semaphore"); + goto error; + } + omx->i_handler_lock = true; + + if (_omx_init_component(omx) < 0) { + goto error; + } + + if (_omx_init_disable_ports(omx) < 0) { + goto error; + } + + return omx; + + error: + omx_encoder_destroy(omx); + return NULL; +} + +void omx_encoder_destroy(struct omx_encoder_t *omx) { + OMX_ERRORTYPE error; + + LOG_INFO("Destroying OMX JPEG encoder ..."); + + component_set_state(&omx->encoder, OMX_StateIdle); + _omx_encoder_clear_ports(omx); + component_set_state(&omx->encoder, OMX_StateLoaded); + + if (omx->i_handler_lock) { + vcos_semaphore_delete(&omx->handler_lock); + } + + if (omx->i_encoder) { + if ((error = OMX_FreeHandle(omx->encoder)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't free OMX.broadcom.image_encode"); + } + } + + if (omx->i_omx) { + OMX_Deinit(); + } + bcm_host_deinit(); + free(omx); +} + +int omx_encoder_set_device(struct omx_encoder_t *omx, struct device_t *dev) { + if (component_set_state(&omx->encoder, OMX_StateIdle) < 0) { + return -1; + } + if (_omx_encoder_clear_ports(omx) < 0) { + return -1; + } + if (_omx_setup_input(omx, dev) < 0) { + return -1; + } + if (_omx_setup_output(omx, dev) < 0) { + return -1; + } + if (component_set_state(&omx->encoder, OMX_StateExecuting) < 0) { + return -1; + } + return 0; +} + +void omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, int index) { + OMX_ERRORTYPE error; + bool loaded = false; + + if ((error = OMX_FillThisBuffer(omx->encoder, omx->output_buffer)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Failed to request filling of the output buffer on encoder"); + assert(0); // TODO + } + + dev->run->pictures[index].size = 0; + omx->output_available = false; + omx->input_required = true; + + while (true) { + assert(!omx->failed); // FIXME + + if (omx->output_available) { + omx->output_available = false; + + memcpy( + dev->run->pictures[index].data + dev->run->pictures[index].size, + omx->output_buffer->pBuffer, + omx->output_buffer->nFilledLen + ); + dev->run->pictures[index].size += omx->output_buffer->nFilledLen; + + if (omx->output_buffer->nFlags & OMX_BUFFERFLAG_ENDOFFRAME) { + omx->output_buffer->nFlags = 0; + break; + } + + if ((error = OMX_FillThisBuffer(omx->encoder, omx->output_buffer)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Failed to request filling of the output buffer on encoder"); + assert(0); // TODO + } + } + + if (omx->input_required) { + omx->input_required = false; + if (loaded) { + continue; + } + loaded = true; + + memcpy(omx->input_buffer->pBuffer, dev->run->hw_buffers[index].start, dev->run->hw_buffers[index].length); + omx->input_buffer->nOffset = 0; + omx->input_buffer->nFilledLen = dev->run->hw_buffers[index].length; + + if ((error = OMX_EmptyThisBuffer(omx->encoder, omx->input_buffer)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Failed to request emptying of the input buffer on encoder"); + assert(0); // TODO + } + } + + vcos_semaphore_wait(&omx->handler_lock); + } +} + +static int _omx_init_component(struct omx_encoder_t *omx) { + OMX_ERRORTYPE error; + + OMX_CALLBACKTYPE callbacks; + MEMSET_ZERO(callbacks); + callbacks.EventHandler = _omx_event_handler; + callbacks.EmptyBufferDone = _omx_input_required_handler; + callbacks.FillBufferDone = _omx_output_available_handler; + + LOG_DEBUG("Initializing OMX.broadcom.image_encode ..."); + if ((error = OMX_GetHandle(&omx->encoder, "OMX.broadcom.image_encode", omx, &callbacks)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't initialize OMX.broadcom.image_encode"); + return -1; + } + omx->i_encoder = true; + return 0; +} + +static int _omx_init_disable_ports(struct omx_encoder_t *omx) { + OMX_ERRORTYPE error; + OMX_INDEXTYPE types[] = { + OMX_IndexParamAudioInit, OMX_IndexParamVideoInit, + OMX_IndexParamImageInit, OMX_IndexParamOtherInit, + }; + OMX_PORT_PARAM_TYPE ports; + + OMX_INIT_STRUCTURE(ports); + if ((error = OMX_GetParameter(omx->encoder, OMX_IndexParamImageInit, &ports)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't OMX_GetParameter(OMX_IndexParamImageInit)"); + return -1; + } + + for (unsigned index = 0; index < 4; ++index) { + if ((error = OMX_GetParameter(omx->encoder, types[index], &ports)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't OMX_GetParameter(types[%u])", index); + return -1; + } + for (OMX_U32 port = ports.nStartPortNumber; port < ports.nStartPortNumber + ports.nPorts; ++port) { + if (component_disable_port(&omx->encoder, port) < 0) { + return -1; + } + } + } + return 0; +} + +static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev) { + OMX_ERRORTYPE error; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + + LOG_DEBUG("Setting up OMX JPEG input port ..."); + + if (component_get_portdef(&omx->encoder, &portdef, INPUT_PORT) < 0) { + LOG_ERROR("... first"); + return -1; + } + + portdef.format.image.nFrameWidth = dev->run->width; + portdef.format.image.nFrameHeight = dev->run->height; + portdef.format.image.nStride = 0; +# define ALIGN(_x, _y) (((_x) + ((_y) - 1)) & ~((_y) - 1)) + portdef.format.image.nSliceHeight = ALIGN(dev->run->height, 16); +# undef ALIGN + portdef.format.image.bFlagErrorConcealment = OMX_FALSE; + portdef.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused; + portdef.nBufferSize = dev->run->max_picture_size; + + switch (dev->run->format) { + // https://www.fourcc.org/yuv.php + //case V4L2_PIX_FMT_YUYV: ??? // FIXME + case V4L2_PIX_FMT_UYVY: portdef.format.image.eColorFormat = OMX_COLOR_FormatCbYCrY; break; + //case V4L2_PIX_FMT_RGB565: format = OMX_COLOR_Format16bitRGB565; break; // FIXME + default: assert(0 && "Unsupported input format for OMX JPEG compressor"); + } + + if (component_set_portdef(&omx->encoder, &portdef) < 0) { + return -1; + } + + if (component_get_portdef(&omx->encoder, &portdef, INPUT_PORT) < 0) { + LOG_ERROR("... second"); + return -1; + } + + if (component_enable_port(&omx->encoder, INPUT_PORT) < 0) { + return -1; + } + omx->i_input_port_enabled = true; + + if ((error = OMX_AllocateBuffer(omx->encoder, &omx->input_buffer, INPUT_PORT, NULL, portdef.nBufferSize)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't allocate OMX JPEG input buffer"); + return -1; + } + return 0; +} + +static int _omx_setup_output(struct omx_encoder_t *omx, struct device_t *dev) { + OMX_ERRORTYPE error; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + OMX_IMAGE_PARAM_QFACTORTYPE quality_factor; + + LOG_DEBUG("Setting up OMX JPEG output port ..."); + + if (component_get_portdef(&omx->encoder, &portdef, OUTPUT_PORT) < 0) { + LOG_ERROR("... first"); + return -1; + } + + portdef.format.image.bFlagErrorConcealment = OMX_FALSE; + portdef.format.image.eCompressionFormat = OMX_IMAGE_CodingJPEG; + portdef.format.image.eColorFormat = OMX_COLOR_FormatYCbYCr; + + if (component_set_portdef(&omx->encoder, &portdef) < 0) { + return -1; + } + + if (component_get_portdef(&omx->encoder, &portdef, OUTPUT_PORT) < 0) { + LOG_ERROR("... second"); + return -1; + } + + OMX_INIT_STRUCTURE(quality_factor); + quality_factor.nPortIndex = OUTPUT_PORT; + quality_factor.nQFactor = dev->jpeg_quality; + + if ((error = OMX_SetParameter(omx->encoder, OMX_IndexParamQFactor, &quality_factor)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't set OMX JPEG quality"); + return -1; + } + + if (component_enable_port(&omx->encoder, OUTPUT_PORT) < 0) { + return -1; + } + omx->i_output_port_enabled = true; + + if ((error = OMX_AllocateBuffer(omx->encoder, &omx->output_buffer, OUTPUT_PORT, NULL, portdef.nBufferSize)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't allocate OMX JPEG output buffer"); + return -1; + } + return 0; +} + +static int _omx_encoder_clear_ports(struct omx_encoder_t *omx) { + OMX_ERRORTYPE error; + int retcode = 0; + + if (omx->i_output_port_enabled) { + retcode -= component_disable_port(&omx->encoder, OUTPUT_PORT); + omx->i_output_port_enabled = false; + } + if (omx->i_input_port_enabled) { + retcode -= component_disable_port(&omx->encoder, INPUT_PORT); + omx->i_input_port_enabled = false; + } + + if (omx->input_buffer) { + if ((error = OMX_FreeBuffer(omx->encoder, INPUT_PORT, omx->input_buffer)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't free OMX JPEG input buffer"); + // retcode -= 1; + } + omx->input_buffer = NULL; + } + if (omx->output_buffer) { + if ((error = OMX_FreeBuffer(omx->encoder, OUTPUT_PORT, omx->output_buffer)) != OMX_ErrorNone) { + LOG_OMX_ERROR(error, "Can't free OMX JPEG output buffer"); + // retcode -= 1; + } + omx->output_buffer = NULL; + } + return retcode; +} + +static OMX_ERRORTYPE _omx_event_handler(UNUSED OMX_HANDLETYPE encoder, + OMX_PTR v_omx, OMX_EVENTTYPE event, OMX_U32 data1, + UNUSED OMX_U32 data2, UNUSED OMX_PTR event_data) { + + // OMX calls this handler for all the events it emits + + struct omx_encoder_t *omx = (struct omx_encoder_t *)v_omx; + + if (event == OMX_EventError) { + LOG_OMX_ERROR((OMX_ERRORTYPE)data1, "OMX error event received"); + omx->failed = true; + vcos_semaphore_post(&omx->handler_lock); + } + return OMX_ErrorNone; +} + +static OMX_ERRORTYPE _omx_input_required_handler(UNUSED OMX_HANDLETYPE encoder, + OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buffer) { + + // Called by OMX when the encoder component requires + // the input buffer to be filled with RAW image data + + struct omx_encoder_t *omx = (struct omx_encoder_t *)v_omx; + + omx->input_required = true; + vcos_semaphore_post(&omx->handler_lock); + return OMX_ErrorNone; +} + +static OMX_ERRORTYPE _omx_output_available_handler(UNUSED OMX_HANDLETYPE encoder, + OMX_PTR v_omx, UNUSED OMX_BUFFERHEADERTYPE *buffer) { + + // Called by OMX when the encoder component has filled + // the output buffer with JPEG data + + struct omx_encoder_t *omx = (struct omx_encoder_t *)v_omx; + + omx->output_available = true; + vcos_semaphore_post(&omx->handler_lock); + return OMX_ErrorNone; +} diff --git a/src/omx/encoder.h b/src/omx/encoder.h new file mode 100644 index 0000000..636677a --- /dev/null +++ b/src/omx/encoder.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include +#include + +#include "../device.h" + + +struct omx_encoder_t { + OMX_HANDLETYPE encoder; + OMX_BUFFERHEADERTYPE *input_buffer; + OMX_BUFFERHEADERTYPE *output_buffer; + bool input_required; + bool output_available; + bool failed; + VCOS_SEMAPHORE_T handler_lock; + + bool i_omx; + bool i_handler_lock; + bool i_encoder; + bool i_input_port_enabled; + bool i_output_port_enabled; +}; + + +struct omx_encoder_t *omx_encoder_init(); +void omx_encoder_destroy(struct omx_encoder_t *omx); + +int omx_encoder_set_device(struct omx_encoder_t *omx, struct device_t *dev); +void omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, int index); diff --git a/src/omx/formatters.c b/src/omx/formatters.c new file mode 100644 index 0000000..ff0de8e --- /dev/null +++ b/src/omx/formatters.c @@ -0,0 +1,83 @@ +/***************************************************************************** +# uStreamer - Lightweight and fast MJPG-HTTP streamer. # +# # +# Copyright (C) 2018 Maxim Devaev # +# # +# 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 3 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, see . # +# # +*****************************************************************************/ + + +#include +#include + +#include +#include + +#include "../tools.h" +#include "formatters.h" + + +#define CASE_TO_STRING(_val) \ + case _val: { return #_val; } + +#define CASE_ASSERT(_msg, _val) default: { \ + char *_buf; A_CALLOC(_buf, 128); \ + sprintf(_buf, _msg ": 0x%08x", _val); \ + assert(0 && _buf); \ + } + + +const char *omx_error_to_string(const OMX_ERRORTYPE error) { + switch (error) { + CASE_TO_STRING(OMX_ErrorNone); + CASE_TO_STRING(OMX_ErrorInsufficientResources); + CASE_TO_STRING(OMX_ErrorUndefined); + CASE_TO_STRING(OMX_ErrorInvalidComponentName); + CASE_TO_STRING(OMX_ErrorComponentNotFound); + CASE_TO_STRING(OMX_ErrorInvalidComponent); + CASE_TO_STRING(OMX_ErrorBadParameter); + CASE_TO_STRING(OMX_ErrorNotImplemented); + CASE_TO_STRING(OMX_ErrorUnderflow); + CASE_TO_STRING(OMX_ErrorOverflow); + CASE_TO_STRING(OMX_ErrorHardware); + CASE_TO_STRING(OMX_ErrorInvalidState); + CASE_TO_STRING(OMX_ErrorStreamCorrupt); + CASE_TO_STRING(OMX_ErrorPortsNotCompatible); + CASE_TO_STRING(OMX_ErrorResourcesLost); + CASE_TO_STRING(OMX_ErrorNoMore); + CASE_TO_STRING(OMX_ErrorVersionMismatch); + CASE_TO_STRING(OMX_ErrorNotReady); + CASE_TO_STRING(OMX_ErrorTimeout); + CASE_TO_STRING(OMX_ErrorSameState); + CASE_TO_STRING(OMX_ErrorResourcesPreempted); + CASE_TO_STRING(OMX_ErrorPortUnresponsiveDuringAllocation); + CASE_TO_STRING(OMX_ErrorPortUnresponsiveDuringDeallocation); + CASE_TO_STRING(OMX_ErrorPortUnresponsiveDuringStop); + CASE_TO_STRING(OMX_ErrorIncorrectStateTransition); + default: return "Unknown OMX error"; + } +} + +const char *omx_state_to_string(const OMX_STATETYPE state) { + switch (state) { + CASE_TO_STRING(OMX_StateLoaded); + CASE_TO_STRING(OMX_StateIdle); + CASE_TO_STRING(OMX_StateExecuting); + CASE_ASSERT("Unsupported OMX state", state); + } +} + +#undef CASE_ASSERT +#undef CASE_TO_STRING diff --git a/src/omx/formatters.h b/src/omx/formatters.h new file mode 100644 index 0000000..66313a4 --- /dev/null +++ b/src/omx/formatters.h @@ -0,0 +1,47 @@ +/***************************************************************************** +# uStreamer - Lightweight and fast MJPG-HTTP streamer. # +# # +# Copyright (C) 2018 Maxim Devaev # +# # +# 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 3 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, see . # +# # +*****************************************************************************/ + + +#pragma once + +#include +#include + +#include +#include + +#include +#include +#include + +#include "../logging.h" +#include "../tools.h" + + +#define LOG_OMX_ERROR(_error, _msg, ...) { \ + LOGGING_LOCK; \ + printf("-- ERROR [%.03Lf tid=%ld] -- " _msg ": %s\n", now_ms_ld(), \ + syscall(SYS_gettid), ##__VA_ARGS__, omx_error_to_string(_error)); \ + LOGGING_UNLOCK; \ + } + + +const char *omx_error_to_string(const OMX_ERRORTYPE error); +const char *omx_state_to_string(const OMX_STATETYPE state);