CXXFLAGS_DEV = $(DEFINES) -DDEV -g -O0 -Wall -Wextra -Wshadow -Werror=return-type -Wmissing-format-attribute -Wdisabled-optimization -Wmissing-declarations -Wmissing-noreturn -Winit-self -Woverloaded-virtual -Wold-style-cast -Wconversion -std=c++14 $(CXXFLAGS_EXTRA) $(INCLUDES)
endif
-CXXFLAGS_REL = $(DEFINES) -O3 -Wall -Werror -std=c++14 $(CXXFLAGS_EXTRA) $(INCLUDES)
+CXXFLAGS_REL = $(DEFINES) -O3 -std=c++14 $(CXXFLAGS_EXTRA) $(INCLUDES)
.PHONY: clean fresh all install strip
dev: vompclient
release: CXXFLAGS := $(CXXFLAGS_REL)
-release: clean vompclient strip
+release: vompclient strip
clean:
rm -f *.o deps vompclient *~ fonts/*.o fonts/*~ teletxt/*.o
--- /dev/null
+/*
+ Copyright 2021 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef EGLPICTURECREATOR_H
+#define EGLPICTURECREATOR_H
+
+class EGLPictureCreator
+{
+ public:
+ virtual bool getEGLPicture(struct OsdVector::PictureInfo& pictureInfo, EGLDisplay* display)=0;
+};
+
+#endif
return false;
}
+ // usleep(100000); -- This delay triggers the race bug
+
// LogNT::getInstance()->debug(TAG,
// "decodePicture 9");
#include "osdvector.h"
#include "videoomx.h"
-
-class EGLPictureCreator {
-public:
- virtual bool getEGLPicture(struct OsdVector::PictureInfo & info, EGLDisplay * display ) = 0;
-};
+#include "eglpicturecreator.h"
--- /dev/null
+/*
+ Copyright 2021 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "osdvector.h"
+
+#include "omx/omximagedecode.h"
+#include "omx/omxeglrender.h"
+
+#include "imageomx2.h"
+
+#include "eglpicturecreator.h"
+
+const static char* TAG = "ImageOMX2";
+
+ImageOMX2::ImageOMX2(/*EGLDisplay t_egl_display*/)
+: OsdVector::PictureDecoder(NULL)
+{
+ log = LogNT::getInstance();
+ //egl_display = t_egl_display;
+}
+
+void ImageOMX2::init()
+{
+ omx_imagedecode = new OMX_Image_Decode();
+ if (!omx_imagedecode->init())
+ {
+ delete omx_imagedecode;
+ omx_imagedecode = NULL;
+ log->crit(TAG, "omx_imagedecode failed to init");
+ }
+
+ omx_eglrender = new OMX_EGL_Render();
+ if (!omx_eglrender->init())
+ {
+ delete omx_imagedecode;
+ delete omx_eglrender;
+ omx_imagedecode = NULL;
+ omx_eglrender = NULL;
+ log->crit(TAG, "omx_eglrender failed to init");
+ }
+}
+
+void ImageOMX2::shutdown()
+{
+ if (omx_imagedecode) omx_imagedecode->shutdown();
+ if (omx_eglrender) omx_eglrender->shutdown();
+ delete omx_imagedecode;
+ delete omx_eglrender;
+ omx_imagedecode = NULL;
+ omx_eglrender = NULL;
+}
+
+void ImageOMX2::reinit()
+{
+ // Only used from main exception handler in decodePicture. *Should* reset everything back to normal
+ shutdown();
+ init();
+}
+
+unsigned char* ImageOMX2::decodePicture(LoadIndex index, unsigned char* buffer, unsigned int length, bool freemem) noexcept
+{
+ // This function needs to return NULL if successful, buffer if not.
+
+ char* rawData{};
+
+ try
+ {
+ if (!omx_imagedecode) throw 1;
+ if (!omx_eglrender) throw 2;
+ if (currentDecodeValid) throw 3;
+ if (length < 3) throw 4;
+ if ((buffer[0] != 0x89) || (buffer[1] != 0x50) || (buffer[2] != 0x4e)) throw 5;
+
+ currentDecodeValid = true;
+
+ currentDecode.lindex = index;
+ currentDecode.decoder = this;
+ currentDecode.type = OsdVector::PictureInfo::RGBAMemBlock;
+ //currentDecode.image/handle = // filled during render
+ //currentDecode.reference = // filled during render
+
+ int rawDataSize{};
+ int rawDataWidth{};
+ int rawDataHeight{};
+ int rawDataStride{};
+
+ decode(reinterpret_cast<char*>(buffer), length, &rawData, &rawDataSize, &rawDataWidth, &rawDataHeight, &rawDataStride);
+
+ log->debug(TAG, "decoded {}, size = {}, w = {}, h = {}, s = {}", static_cast<void*>(rawData), rawDataSize, rawDataWidth, rawDataHeight, rawDataStride);
+
+ currentDecode.width = rawDataWidth;
+ currentDecode.height = rawDataHeight;
+
+ render(rawData, rawDataSize, rawDataWidth, rawDataHeight, rawDataStride);
+ free(rawData);
+
+ // By the interface, if freemem = true, free buffer before returning
+ if (freemem) free(buffer);
+ return NULL;
+ }
+ catch (int e)
+ {
+ switch(e)
+ {
+ case 1: log->error(TAG, "omx_imagedecode not initted"); break;
+ case 2: log->error(TAG, "omx_eglrender not initted"); break;
+ case 3: log->error(TAG, "Program error, currentDecodeValid = true on call to decodePicture"); break;
+ case 4: log->error(TAG, "Data length too short"); break;
+ case 5: log->error(TAG, "Input data not in PNG format"); break;
+ case 101: log->error(TAG, "Received all image from image_decode but error with nFlags and EOS"); break;
+ case 201: log->error(TAG, "Could not get egl picture creator"); break;
+ case 202: log->error(TAG, "Create egl picture failed"); break;
+ }
+
+ if (rawData) free(rawData);
+ reinit();
+ return buffer;
+ }
+ catch (OMX_Exception& e)
+ {
+ log->error(TAG, "OMX Exception");
+ log->error(TAG, "{:#x} - {}", e.errorCode(), e.what());
+
+ if (rawData) free(rawData);
+ reinit();
+ return buffer;
+ }
+ catch (...)
+ {
+ log->error(TAG, "Other exception caught in decodePicture?!");
+
+ if (rawData) free(rawData);
+ reinit();
+ return buffer;
+ }
+}
+
+bool ImageOMX2::getDecodedPicture(struct OsdVector::PictureInfo& pict_inf)
+{
+ if (!currentDecodeValid) return false;
+ pict_inf = currentDecode;
+ currentDecodeValid = false;
+ memset(¤tDecode, 0, sizeof(currentDecode));
+ return true;
+}
+
+// Internal functions
+
+void ImageOMX2::decode(char* inputData, int inputDataSize, char** outRawData, int* outRawDataSize, int* outWidth, int* outHeight, int* outStride)
+{
+ std::vector<char*> chunks;
+ std::vector<int> sizes;
+
+ try
+ {
+ // Talk to image_decode
+
+ log->debug(TAG, "image_decode: disable input");
+ omx_imagedecode->disableInput();
+ log->debug(TAG, "image_decode: disable output");
+ omx_imagedecode->disableOutput();
+ log->debug(TAG, "image_decode: change state to idle");
+ omx_imagedecode->changeState(OMX_StateIdle);
+ log->debug(TAG, "image_decode: set format");
+ omx_imagedecode->setFormat();
+ log->debug(TAG, "image_decode: prepare input buffers");
+ omx_imagedecode->prepareInputBuffers(inputDataSize);
+ log->debug(TAG, "image_decode: enable input");
+ omx_imagedecode->enableInput(false); // Don't wait for this, it depends on OMX_UseBuffer being called next
+ log->debug(TAG, "image_decode: allocate input buffers");
+ omx_imagedecode->allocateInputBuffers(inputData);
+
+ log->debug(TAG, "image_decode: change state to executing");
+ omx_imagedecode->changeState(OMX_StateExecuting);
+
+ log->debug(TAG, "image_decode: sendtoinput");
+ omx_imagedecode->sendToInput();
+ log->debug(TAG, "image_decode: wait for port settings change");
+ omx_imagedecode->waitForOutputPortSettingsChange();
+ log->debug(TAG, "image_decode: fix slice height");
+ omx_imagedecode->setSliceHeight(16); // FIXME allow full height if imageHeight % 16 = 0 ??
+ log->debug(TAG, "image_decode: enable output");
+ omx_imagedecode->enableOutput(false); // Don't wait for this, I hope it depends on OMX_UseBuffer being called next
+ log->debug(TAG, "image_decode: allocate output buffer");
+ omx_imagedecode->allocateOutputBuffer();
+
+ int xwidth{};
+ int xheight{};
+ int xstride{};
+ int xsliceHeight{};
+
+ omx_imagedecode->getImageInfo(&xwidth, &xheight, &xstride, &xsliceHeight);
+ log->debug(TAG, "ImageInfo: {} {} {} {}", xwidth, xheight, xstride, xsliceHeight);
+
+ int linesGot = 0;
+ int linesToGet;
+ while(1)
+ {
+ char* data;
+ int nFlags;
+ log->debug(TAG, "image_decode: receiveFromOutput");
+ omx_imagedecode->receiveFromOutput(&data, &nFlags);
+ log->debug(TAG, "image_decode: receiveFromOutput {:#x}", nFlags);
+
+
+ linesToGet = xheight - linesGot;
+ if (linesToGet > xsliceHeight) linesToGet = xsliceHeight;
+
+ chunks.push_back(data);
+ sizes.push_back(linesToGet * xstride);
+
+ linesGot += linesToGet;
+
+ if (linesGot == xheight)
+ {
+ if ((nFlags & OMX_BUFFERFLAG_EOS) == 0) throw 101;
+ break;
+ }
+ }
+
+ log->debug(TAG, "image_decode: change state to idle");
+ omx_imagedecode->changeState(OMX_StateIdle);
+ log->debug(TAG, "image_decode: disable input");
+ omx_imagedecode->disableInput(false);
+ log->debug(TAG, "image_decode: disable output");
+ omx_imagedecode->disableOutput(false);
+ log->debug(TAG, "image_decode: deallocate input buffers");
+ omx_imagedecode->deallocateInputBuffers();
+ log->debug(TAG, "image_decode: deallocate output buffer");
+ omx_imagedecode->deallocateOutputBuffer();
+ log->debug(TAG, "image_decode: change state to loaded");
+ omx_imagedecode->changeState(OMX_StateLoaded);
+
+ int totalSize{};
+ for (int chunkSize : sizes) totalSize += chunkSize;
+ log->debug(TAG, "total decoded mem chunks size: {}", totalSize);
+ char* gluedTogether = static_cast<char*>(malloc(totalSize));
+ log->debug(TAG, "gluedTogether {}", static_cast<void*>(gluedTogether));
+ char* dest = gluedTogether;
+ for (unsigned int i = 0 ; i < chunks.size(); i++)
+ {
+ memcpy(dest, chunks[i], sizes[i]);
+ dest += sizes[i];
+ }
+
+ for(char* chunk : chunks) free(chunk);
+
+ *outRawDataSize = totalSize;
+ *outRawData = gluedTogether;
+ *outWidth = xwidth;
+ *outHeight = xheight;
+ *outStride = xstride;
+ }
+ catch (...)
+ {
+ for(char* chunk : chunks) free(chunk);
+ throw;
+ }
+}
+
+void ImageOMX2::render(char* inputData, int inputDataSize, int imageWidth, int imageHeight, int imageStride)
+{
+ log->debug(TAG, "render: inputdataSize = {}", inputDataSize);
+
+ EGLPictureCreator* pictcreat = dynamic_cast<EGLPictureCreator*>(Osd::getInstance());
+ if (!pictcreat) throw 201;
+ EGLDisplay egl_display2;
+ if (!pictcreat->getEGLPicture(currentDecode, &egl_display2)) throw 202;
+
+ // pictcreat must read width, height from currentDecode
+ // The VGImage handle returned from vgCreateImage is stored in currentDecode.handle
+ // The EGLImageKHR returned from eglCreateImageKHR is stored in currentDecode.reference
+
+ log->debug(TAG, "egl_render: disable input");
+ omx_eglrender->disableInput();
+ log->debug(TAG, "egl_render: disable output");
+ omx_eglrender->disableOutput();
+ log->debug(TAG, "egl_render: prep");
+ omx_eglrender->prepareOutputPort(egl_display2);
+ log->debug(TAG, "egl_render: change state to idle");
+ omx_eglrender->changeState(OMX_StateIdle);
+ log->debug(TAG, "egl_render: prepareInputPort");
+ omx_eglrender->prepareInputPort(imageWidth, imageHeight, imageStride);
+ log->debug(TAG, "egl_render: enable input");
+ omx_eglrender->enableInput(false);
+ log->debug(TAG, "egl_render: allocate input buffers");
+ omx_eglrender->allocateInputBuffers(inputData);
+ log->debug(TAG, "Print port settings");
+ omx_eglrender->printPortSettings(false);
+ log->debug(TAG, "egl_render: enable output");
+ omx_eglrender->enableOutput(false);
+ log->debug(TAG, "egl_render: allocate output buffer");
+ omx_eglrender->allocateEGLImageKHR(currentDecode.reference);
+ log->debug(TAG, "egl_render: change state to executing");
+ omx_eglrender->changeState(OMX_StateExecuting);
+ log->debug(TAG, "egl_render: sendtoinput");
+ omx_eglrender->sendToInput(inputData, inputDataSize);
+
+ omx_eglrender->printPortSettings(true);
+
+ log->debug(TAG, "egl_render: render");
+ omx_eglrender->render();
+ log->debug(TAG, "egl_render: render done");
+
+
+ log->debug(TAG, "egl_render: change state to idle");
+ omx_eglrender->changeState(OMX_StateIdle);
+
+ log->debug(TAG, "egl_render: flush input commands");
+ omx_eglrender->flushInputCommands();
+ log->debug(TAG, "egl_render: flush output commands");
+ omx_eglrender->flushOutputCommands();
+ log->debug(TAG, "egl_render: disable input");
+ omx_eglrender->disableInput(false);
+ log->debug(TAG, "egl_render: disable output");
+ omx_eglrender->disableOutput(false);
+ log->debug(TAG, "egl_render: deallocate output buffer header");
+ omx_eglrender->deallocateEGLImageKHR();
+ log->debug(TAG, "egl_render: deallocate input buffer header");
+ omx_eglrender->deallocateInputBuffers();
+ log->debug(TAG, "egl_render: change state to loaded");
+ omx_eglrender->changeState(OMX_StateLoaded);
+}
--- /dev/null
+/*
+ Copyright 2021 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/*
+
+TL;DR:
+
+The OMX image_decode "hardware" is buggy when given an image with height not divisible
+by 16. Extensive testing shows there is no way to use tunneled communication between
+image_decode and egl_render when the image height is not divisible by 16 and guarantee
+correct image display.
+
+ImageOMX1 mitigated this by cropping the image height to a value divisible by 16
+but this introduced a new race condition bug which corrupted some images.
+ImageOMX1 works most of the time because it happens to pull down the tunnel before
+it has finished. If a 0.1s delay is inserted after the
+ if (!video->ChangeComponentState(omx_egl_render,OMX_StateExecuting)) {
+if block, it will break every image with height not divisible by 16.
+
+The solution is to not use tunneled communication between image_decode and
+egl_render. If the input image height is not divisible by 16, set the slice height
+to 16. Then feed egl_render separately.
+
+This ImageOMX2 is a rework of my clean-implementation test programs used to debug
+the problem. Future work: Fix ImageOMX1 or swap over to ImageOMX2 if it works
+well enough? (Is it even possible to run image_decode/egl_render without integration
+with VideoOMX?)
+
+*/
+
+#ifndef IMAGEOMX2_H
+#define IMAGEOMX2_H
+
+#include "osdvector.h"
+#include "osdopenvg.h"
+
+class LogNT;
+class OMX_Image_Decode;
+class OMX_EGL_Render;
+
+class ImageOMX2 : public OsdVector::PictureDecoder
+{
+// PictureDecoder Interface
+ public:
+ ImageOMX2(/*EGLDisplay egl_display*/);
+ virtual ~ImageOMX2() {};
+
+ void init();
+ void shutdown();
+
+ unsigned char* decodePicture(LoadIndex index, unsigned char* buffer, unsigned int length, bool freemem) noexcept;
+ bool getDecodedPicture(struct OsdVector::PictureInfo& pict_inf);
+ void freeReference(void*) {};
+
+// Internal stuff
+
+ private:
+ LogNT* log{};
+ //EGLDisplay egl_display;
+ OMX_Image_Decode* omx_imagedecode{};
+ OMX_EGL_Render* omx_eglrender{};
+
+ // State data for the decode
+ struct OsdVector::PictureInfo currentDecode{}; // There's only one user so no locking required
+ bool currentDecodeValid{};
+
+ // Internal functions
+
+ void reinit();
+ void decode(char* inputData, int inputDataSize, char** outRawData, int* outRawDataSize, int* outWidth, int* outHeight, int* outStride); // throws
+ void render(char* inputData, int inputDataSize, int imageWidth, int imageHeight, int imageStride); // throws
+};
+
+#endif
OBJ_RASPBERRY = main.o threadp.o osdopenvg.o \
ledraspberry.o videoomx.o audioomx.o imageomx.o \
- wjpegsimple.o inputlinux.o inputcec.o
+ wjpegsimple.o inputlinux.o inputcec.o \
+ omx/omx.o omx/omximagedecode.o omx/omxeglrender.o imageomx2.o
OBJ_WINDOWS = winmain.o threadwin.o inputwin.o ledwin.o videowin.o \
audiowin.o windowsosd.o dsallocator.o dssourcefilter.o dssourcepin.o \
--- /dev/null
+/*
+ Copyright 2021 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "../log.h"
+#include "omximagedecode.h"
+#include "omxeglrender.h"
+
+#include "omx.h"
+
+static const char* TAG = "OMX";
+
+
+// Static variable storage
+OMX_Image_Decode* OMX::omx_image_decode{};
+OMX_EGL_Render* OMX::omx_egl_render{};
+
+OMX_HANDLETYPE OMX::handle_image_decode{};
+OMX_HANDLETYPE OMX::handle_egl_render{};
+
+std::thread OMX::eventsProcessorThread;
+std::mutex OMX::eventsProcessorMutex;
+std::condition_variable OMX::eventsProcessorCond;
+std::mutex OMX::eventsMutex;
+Events OMX::events;
+bool OMX::eventsProcessorWake{};
+bool OMX::eventsProcessorShutdownNow{};
+int OMX::eventsProcessorUsageCount{};
+EventWaiters OMX::eventWaiters;
+std::mutex OMX::eventWaitersMutex;
+
+
+// Main static callbacks
+
+OMX_ERRORTYPE OMX::scb_EventHandler(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_EVENTTYPE event_type,
+ OMX_IN OMX_U32 data1, OMX_IN OMX_U32 data2, OMX_IN OMX_PTR event_data)
+{
+ LogNT::getInstance()->debug("OMXSCB", "EventHandler {} {} {} {}", static_cast<void*>(handle_image_decode), static_cast<void*>(omx_image_decode), static_cast<void*>(handle_egl_render), static_cast<void*>(omx_egl_render));
+ if (handle == handle_image_decode)
+ return omx_image_decode->cb_EventHandler(handle, appdata, event_type, data1, data2, event_data);
+ else if (handle == handle_egl_render)
+ return omx_egl_render->cb_EventHandler(handle, appdata, event_type, data1, data2, event_data);
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE OMX::scb_EmptyBufferDone(OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
+{
+ LogNT::getInstance()->debug("OMXSCB", "EmptyBufferDone {} {} {} {}", static_cast<void*>(handle_image_decode), static_cast<void*>(omx_image_decode), static_cast<void*>(handle_egl_render), static_cast<void*>(omx_egl_render));
+ if (handle == handle_image_decode)
+ return omx_image_decode->cb_EmptyBufferDone(handle, appdata, buffer);
+ else if (handle == handle_egl_render)
+ return omx_egl_render->cb_EmptyBufferDone(handle, appdata, buffer);
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE OMX::scb_FillBufferDone(OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
+{
+ LogNT::getInstance()->debug("OMXSCB", "FillBufferDone {} {} {} {}", static_cast<void*>(handle_image_decode), static_cast<void*>(omx_image_decode), static_cast<void*>(handle_egl_render), static_cast<void*>(omx_egl_render));
+ if (handle == handle_image_decode)
+ return omx_image_decode->cb_FillBufferDone(handle, appdata, buffer);
+ else if (handle == handle_egl_render)
+ return omx_egl_render->cb_FillBufferDone(handle, appdata, buffer);
+ return OMX_ErrorNone;
+}
+
+// ---
+
+OMX::OMX()
+{
+ log = LogNT::getInstance();
+}
+
+OMX::~OMX()
+{
+}
+
+// bool OMX::OMX_Master_Init()
+// {
+// OMX_ERRORTYPE error;
+// error = OMX_Init();
+// if (error != OMX_ErrorNone) return false;
+// return true;
+// }
+
+OMX_ERRORTYPE OMX::cb_EventHandler(OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_EVENTTYPE event_type, OMX_IN OMX_U32 data1, OMX_IN OMX_U32 data2, OMX_IN OMX_PTR event_data)
+{
+ //Log* l = Log::getInstance();
+ log->debug(TAG, "CB: eventHandler {} {} {:#x} {:#x} {}", static_cast<void*>(handle), event_type, data1, data2, static_cast<void*>(event_data));
+
+ eventsMutex.lock();
+ Event* incomingEvent = new Event{.appdata = appdata, .eventType = event_type, .data1 = data1, .data2 = data2, .event_data = event_data};
+ events.push_back(incomingEvent);
+ eventsMutex.unlock();
+
+ eventsProcessorMutex.lock();
+ eventsProcessorWake = true;
+ eventsProcessorMutex.unlock();
+ eventsProcessorCond.notify_one();
+
+ log->debug(TAG, "eventHandler end");
+ return OMX_ErrorNone;
+}
+
+void OMX::initEventsProcessing()
+{
+ eventsProcessorMutex.lock();
+
+ if (eventsProcessorUsageCount == 0)
+ {
+ eventsProcessorUsageCount = 1;
+
+ eventsProcessorThread = std::thread( [&]
+ {
+ eventsProcessorMutex.lock();
+ eventsProcessorMutex.unlock();
+ eventsProcessorLoop();
+ });
+ }
+ else
+ {
+ ++eventsProcessorUsageCount;
+ }
+
+ eventsProcessorMutex.unlock();
+}
+
+void OMX::stopEventsProcessing()
+{
+ eventsProcessorMutex.lock();
+
+ --eventsProcessorUsageCount;
+
+ if (eventsProcessorUsageCount == 0)
+ {
+ // stop event loop
+ eventsProcessorWake = true;
+ eventsProcessorShutdownNow = true;
+ eventsProcessorCond.notify_one();
+ eventsProcessorMutex.unlock();
+ eventsProcessorThread.join();
+ }
+ else
+ {
+ eventsProcessorMutex.unlock();
+ }
+}
+
+void OMX::eventsProcessorLoop()
+{
+ LogNT* log = LogNT::getInstance();
+ std::unique_lock<std::mutex> ul(eventsProcessorMutex); // locks
+
+ while(1)
+ {
+ // locked
+ if (eventsProcessorWake)
+ {
+ eventsProcessorWake = false;
+ ul.unlock();
+
+ if (eventsProcessorShutdownNow) return;
+
+ eventsMutex.lock();
+ for (Events::iterator eventIterator = events.begin(); eventIterator != events.end(); ) // for each event received from OMX
+ {
+ Event* event = *eventIterator;
+
+ if (event->isNew)
+ {
+ event->isNew = false;
+
+ log->debug(TAG, "Event Processor Loop - event:");
+
+ if (event->eventType == 0)
+ log->debug(TAG, "* eventType: OMX_EventCmdComplete");
+ else if (event->eventType == 1)
+ log->debug(TAG, "* eventType: OMX_EventError");
+ else if (event->eventType == 2)
+ log->debug(TAG, "* eventType: OMX_EventMark");
+ else if (event->eventType == 3)
+ log->debug(TAG, "* eventType: OMX_EventPortSettingsChanged");
+ else if (event->eventType == 4)
+ log->debug(TAG, "* eventType: OMX_EventBufferFlag");
+ else
+ log->debug(TAG, "* eventType: {:#x}", event->eventType);
+
+ if (event->eventType == OMX_EventCmdComplete)
+ {
+ if (event->data1 == OMX_CommandStateSet)
+ log->debug(TAG, "* OMX_COMMANDTYPE: OMX_CommandStateSet");
+ if (event->data1 == OMX_CommandFlush)
+ log->debug(TAG, "* OMX_COMMANDTYPE: OMX_CommandFlush");
+ if (event->data1 == OMX_CommandPortDisable)
+ log->debug(TAG, "* OMX_COMMANDTYPE: OMX_CommandPortDisable");
+ if (event->data1 == OMX_CommandPortEnable)
+ log->debug(TAG, "* OMX_COMMANDTYPE: OMX_CommandPortEnable");
+ if (event->data1 == OMX_CommandMarkBuffer)
+ log->debug(TAG, "* OMX_COMMANDTYPE: OMX_CommandMarkBuffer");
+
+ if (event->data1 == OMX_CommandStateSet)
+ {
+ log->debug(TAG, "* new state: {}", event->data2);
+ }
+
+ if ( (event->data1 == OMX_CommandPortEnable) || (event->data1 == OMX_CommandPortDisable) )
+ {
+ log->debug(TAG, "* port en/disabled: {}", event->data2);
+ }
+ }
+ else if (event->eventType == OMX_EventBufferFlag)
+ {
+ log->debug(TAG, "* Port: {:#x} {}", event->data1, event->data1);
+ }
+ else if (event->eventType == OMX_EventError)
+ {
+ log->debug(TAG, "* Error code: {:#x}", event->data1);
+
+ if (event->data1 == OMX_ErrorPortUnpopulated)
+ log->debug(TAG, "* = : OMX_ErrorPortUnpopulated");
+ if (event->data1 == OMX_ErrorInsufficientResources)
+ log->debug(TAG, "* =: OMX_ErrorInsufficientResources");
+ }
+ } // end if event is new (then print it out)
+
+
+ // Find matching EventWaiter
+
+ eventWaitersMutex.lock();
+
+ // Now have EventsMutex and eventWaitersMutex
+
+ EventWaiters::iterator eventWaiterIterator;
+ EventWaiter* eventWaiter{};
+
+ for (eventWaiterIterator = eventWaiters.begin(); eventWaiterIterator < eventWaiters.end(); eventWaiterIterator++)
+ {
+ // structure here not great yet
+ // not looking at eventWaiter->eventType??
+
+ eventWaiter = *eventWaiterIterator;
+
+ if (event->eventType == OMX_EventCmdComplete)
+ {
+ if ( ((event->data1 == OMX_CommandPortEnable) && (eventWaiter->command == OMX_CommandPortEnable))
+ || ((event->data1 == OMX_CommandPortDisable) && (eventWaiter->command == OMX_CommandPortDisable)) )
+ {
+ if (event->data2 == eventWaiter->port)
+ {
+ // Found!
+ eventWaiter->receivedEvent = event;
+ break;
+ }
+ }
+ else if ((event->data1 == OMX_CommandStateSet) && (eventWaiter->command == OMX_CommandStateSet))
+ {
+ if (event->data2 == eventWaiter->newState)
+ {
+ // Found!
+ eventWaiter->receivedEvent = event;
+ break;
+ }
+ }
+ else if ((event->data1 == OMX_CommandFlush) && (eventWaiter->command == OMX_CommandFlush))
+ {
+ if (event->data2 == eventWaiter->flushPort)
+ {
+ // Found!
+ eventWaiter->receivedEvent = event;
+ break;
+ }
+ }
+ }
+ else if (event->eventType == OMX_EventPortSettingsChanged)
+ {
+ if ( (eventWaiter->eventType == OMX_EventPortSettingsChanged)
+ && (event->data1 == eventWaiter->port) )
+ {
+ // Found!
+ eventWaiter->receivedEvent = event;
+ break;
+ }
+ }
+ } // end of each eventWaiter loop
+
+
+ // Now obv we have an event, but do we have an eventWaiter? If not, advance and continue
+ if (!eventWaiter || (eventWaiterIterator == eventWaiters.end())) // A) none in the loop at all, B) none matched
+ {
+ eventWaitersMutex.unlock();
+
+ eventIterator++;
+ continue;
+ }
+
+ // Now we have an event (event, eventIterator) and a found eventWaiter (eventWaiter, eventWaiterIterator)
+
+ // Remove the eventWaiter from the deque
+ eventWaiters.erase(eventWaiterIterator);
+ eventWaitersMutex.unlock();
+
+ // Remove the event from the deque
+ eventIterator = events.erase(eventIterator);
+
+ // Iterators are now dead.
+ // Now we have an event and an eventWaiter
+ // Still have the EventsMutex but we have released the eventWaitersMutex.
+
+ log->debug(TAG, "This event {} found an event waiter for it {}", static_cast<void*>(event), static_cast<void*>(eventWaiter));
+
+
+ if (eventWaiter->waiting)
+ {
+ // FIXME look at whether eventWaiter could miss this signal
+ log->debug(TAG, "Notifying eventWaiter: {} on cond {}", static_cast<void*>(eventWaiter), static_cast<void*>(&eventWaiter->cond));
+ eventWaiter->cond.notify_one();
+ }
+ else
+ {
+ log->debug(TAG, "EventWaiter not waiting. Deleting {}", static_cast<void*>(eventWaiter));
+ delete event;
+ delete eventWaiter;
+ }
+
+ log->debug(TAG, "Done processing eventWaiter, goaround events loop");
+
+
+ } // end for each received event
+
+ // FIXME If there are events still in events that have not been matched up and sent
+ // to an eventWaiter, or that were not marked as waiting so we deleted it here, then this is a bug
+ eventsMutex.unlock();
+
+ // Reacquire eventsProcessor lock
+ ul.lock();
+ } // end if event processor wake
+
+ // locked
+
+ eventsProcessorCond.wait(ul, [&] { return eventsProcessorWake; });
+
+ // relocked, loop
+ } // while(1)
+}
+
+void OMX::enablePort(OMX_U32 port, bool enable, bool wait)
+{
+ OMX_ERRORTYPE error;
+
+ OMX_PARAM_PORTDEFINITIONTYPE pdt;
+ memset(&pdt, 0, sizeof(pdt));
+ pdt.nSize = sizeof(pdt);
+ pdt.nVersion.nVersion = OMX_VERSION;
+ pdt.nPortIndex = port;
+ error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &pdt);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX::enablePort()", error);
+
+ if (pdt.bEnabled == enable) return; // already in requested state
+
+ std::unique_lock<std::mutex> ul(eventWaitersMutex);
+
+ OMX_COMMANDTYPE command = enable ? OMX_CommandPortEnable : OMX_CommandPortDisable;
+
+ log->debug(TAG, "en/disablePort: port: {:#x}, command: {:#x}", port, command);
+ error = OMX_SendCommand(componentHandle, command, port, 0);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SendCommand in OMX::enablePort()", error);
+ log->debug(TAG, "en/disablePort: port: {:#x}, command: {:#x} done", port, command);
+
+ EventWaiter* eventWaiter = new EventWaiter();
+ eventWaiter->waiting = wait;
+ eventWaiter->command = command;
+ eventWaiter->port = port;
+
+ eventWaiters.push_back(eventWaiter);
+
+ if (!wait) return; // && unlock. eventWaiter becomes owned by eventProcessor thread
+
+ log->debug(TAG, "en/disablePort: Going to wait on cond {}", static_cast<void*>(&eventWaiter->cond));
+ eventWaiter->cond.wait(ul); // sleep this thread
+ log->debug(TAG, "en/disablePort: Back from wait on cond {}", static_cast<void*>(&eventWaiter->cond));
+ ul.unlock();
+
+
+ if (eventWaiter->receivedEvent)
+ {
+ // The event processor thread received an event and saved it here
+ log->debug(TAG, "received event!");
+ delete eventWaiter->receivedEvent;
+ delete eventWaiter;
+ }
+ else
+ {
+ delete eventWaiter; // This is ours, at least we can clean this up
+ throw OMX_Exception("Failed to receive event!", 0);
+ }
+}
+
+void OMX::changeState(OMX_STATETYPE reqState, bool wait)
+{
+ OMX_STATETYPE currentState;
+ OMX_GetState(componentHandle, ¤tState);
+ log->debug(TAG, "Current state: {}", currentState);
+ if (currentState == reqState)
+ {
+ log->debug(TAG, "changeState return immediately, already in reqState");
+ return;
+ }
+
+ std::unique_lock<std::mutex> ul(eventWaitersMutex);
+ OMX_ERRORTYPE error = OMX_SendCommand(componentHandle, OMX_CommandStateSet, reqState, 0);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SendCommand in OMX::changeState()", error);
+
+ EventWaiter* eventWaiter = new EventWaiter();
+ eventWaiter->waiting = wait;
+ eventWaiter->command = OMX_CommandStateSet;
+ eventWaiter->newState = reqState;
+
+ eventWaiters.push_back(eventWaiter);
+
+ if (!wait) return; // && unlock. eventWaiter becomes owned by eventProcessor thread
+
+ log->debug(TAG, "changeState: Going to wait on cond {}", static_cast<void*>(&eventWaiter->cond));
+ eventWaiter->cond.wait(ul); // sleep this thread
+ log->debug(TAG, "changeState: Back from wait on cond {}", static_cast<void*>(&eventWaiter->cond));
+ ul.unlock();
+
+ if (eventWaiter->receivedEvent)
+ {
+ // The event processor thread received an event and saved it here
+ log->debug(TAG, "received event!");
+ delete eventWaiter->receivedEvent;
+ delete eventWaiter;
+ }
+ else
+ {
+ delete eventWaiter; // This is ours, at least we can clean this up
+ throw OMX_Exception("Failed to receive event!", 0);
+ }
+}
+
+void OMX::flushCommands(OMX_U32 port, bool wait)
+{
+ std::unique_lock<std::mutex> ul(eventWaitersMutex);
+
+ OMX_ERRORTYPE error = OMX_SendCommand(componentHandle, OMX_CommandFlush, port, NULL);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SendCommand in OMX::flushCommands()", error);
+
+ EventWaiter* eventWaiter = new EventWaiter();
+ eventWaiter->waiting = wait;
+ eventWaiter->command = OMX_CommandFlush;
+ eventWaiter->flushPort = port;
+
+ eventWaiters.push_back(eventWaiter);
+
+ if (!wait) return; // && unlock. eventWaiter becomes owned by eventProcessor thread
+
+ log->debug(TAG, "flushCommands: Going to wait on cond {}", static_cast<void*>(&eventWaiter->cond));
+ eventWaiter->cond.wait(ul); // sleep this thread
+ log->debug(TAG, "flushCommands: Back from wait on cond {}", static_cast<void*>(&eventWaiter->cond));
+ ul.unlock();
+
+ if (!eventWaiter->receivedEvent)
+ {
+ delete eventWaiter;
+ throw OMX_Exception("Error waiting for event in flushCommands", 0);
+ }
+
+ // The event processor thread received an event and saved it here
+ log->debug(TAG, "received event!");
+ delete eventWaiter->receivedEvent;
+ delete eventWaiter;
+}
--- /dev/null
+/*
+ Copyright 2021 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef OMX_H
+#define OMX_H
+
+#include <deque>
+#include <mutex>
+#include <thread>
+#include <condition_variable>
+#include <exception>
+
+#include <bcm_host.h>
+
+#define OMX_SKIP64BIT
+#include <IL/OMX_Core.h>
+#include <IL/OMX_Types.h>
+#include <IL/OMX_Component.h>
+#include <IL/OMX_Broadcom.h>
+
+struct Event
+{
+ OMX_PTR appdata;
+ OMX_EVENTTYPE eventType;
+ OMX_U32 data1;
+ OMX_U32 data2;
+ OMX_PTR event_data;
+
+ bool isNew{true};
+};
+
+struct EventWaiter
+{
+ bool waiting;
+ OMX_EVENTTYPE eventType;
+ OMX_COMMANDTYPE command;
+ OMX_U32 port;
+ OMX_STATETYPE newState;
+ OMX_U32 flushPort;
+ std::condition_variable cond;
+ Event* receivedEvent{};
+};
+
+struct BufferWithOutputPort
+{
+ OMX_BUFFERHEADERTYPE* bufhead;
+ std::condition_variable cond;
+ std::mutex mutex;
+ bool done{};
+};
+
+using Events = std::deque<Event*>;
+using EventWaiters = std::deque<EventWaiter*>;
+
+class OMX_Exception : public std::exception
+{
+ public:
+ OMX_Exception(const char* tdesc, uint32_t terrCode) : desc(tdesc), errCode(terrCode) {}
+ const char* what() const noexcept { return desc; };
+ uint32_t errorCode() { return errCode; }
+ private:
+ const char* desc{};
+ uint32_t errCode{};
+};
+
+
+class LogNT;
+class OMX_Image_Decode;
+class OMX_EGL_Render;
+
+class OMX
+{
+ public:
+ OMX();
+ ~OMX();
+
+ //static bool OMX_Master_Init();
+
+ virtual bool init()=0;
+ virtual void shutdown()=0;
+
+ void enablePort(OMX_U32 port, bool enable, bool wait); // throws
+ void changeState(OMX_STATETYPE type, bool wait = true); // throws
+
+ // Static event callbacks
+
+ static OMX_ERRORTYPE scb_EventHandler(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_EVENTTYPE event_type,
+ OMX_IN OMX_U32 data1, OMX_IN OMX_U32 data2, OMX_IN OMX_PTR event_data);
+
+ static OMX_ERRORTYPE scb_FillBufferDone(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer);
+
+ static OMX_ERRORTYPE scb_EmptyBufferDone(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer);
+
+ static constexpr const char* componentName_image_decode = "OMX.broadcom.image_decode";
+ static constexpr const char* componentName_egl_render = "OMX.broadcom.egl_render";
+
+ protected:
+ LogNT* log;
+
+ void flushCommands(OMX_U32 port, bool wait = true); // throws
+
+ // static singleton object pointers
+ static OMX_Image_Decode* omx_image_decode;
+ static OMX_EGL_Render* omx_egl_render;
+
+ // static component handles provided by OMX
+ static OMX_HANDLETYPE handle_image_decode;
+ static OMX_HANDLETYPE handle_egl_render;
+
+ // A non-static copy of the above
+ OMX_HANDLETYPE componentHandle;
+
+ // In-object callbacks
+
+ virtual OMX_ERRORTYPE cb_EventHandler(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_EVENTTYPE event_type,
+ OMX_IN OMX_U32 data1, OMX_IN OMX_U32 data2, OMX_IN OMX_PTR event_data) final;
+
+ virtual OMX_ERRORTYPE cb_FillBufferDone(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)=0;
+
+ virtual OMX_ERRORTYPE cb_EmptyBufferDone(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)=0;
+
+ /* Incoming events processing stuff
+ *
+ * Events are handled entirely by the base class which runs a thread to match incoming
+ * events with a class and a particular sleeping calling thread. It will wake the correct
+ * call, if wait = true in the original call. If not it will just delete the incoming event.
+ *
+ * FillBufferDone and EmptyBufferDone will call the derived object's functions.
+ */
+
+ static void initEventsProcessing();
+ static void stopEventsProcessing();
+
+ static Events events;
+ static std::mutex eventsMutex;
+ static std::mutex eventsProcessorMutex;
+ static std::thread eventsProcessorThread;
+ static std::condition_variable eventsProcessorCond;
+ static bool eventsProcessorWake;
+ static bool eventsProcessorShutdownNow;
+ static int eventsProcessorUsageCount;
+ static void eventsProcessorLoop();
+
+ static EventWaiters eventWaiters;
+ static std::mutex eventWaitersMutex;
+
+};
+
+#endif
--- /dev/null
+/*
+ Copyright 2021 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <VG/openvg.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "../log.h"
+
+#include "omxeglrender.h"
+
+static const char* TAG = "OMX_EGL_Render";
+
+
+OMX_EGL_Render::OMX_EGL_Render()
+{
+ omx_egl_render = this;
+}
+
+OMX_EGL_Render::~OMX_EGL_Render()
+{
+ omx_image_decode = NULL;
+ handle_egl_render = 0;
+}
+
+bool OMX_EGL_Render::init()
+{
+ initEventsProcessing();
+
+ OMX_CALLBACKTYPE callbacks = {&scb_EventHandler, &scb_EmptyBufferDone, &scb_FillBufferDone};
+
+ char* componentName;
+ asprintf(&componentName, "%s", componentName_egl_render);
+ OMX_ERRORTYPE error = OMX_GetHandle(&componentHandle, componentName, NULL, &callbacks);
+ free(componentName);
+ log->debug(TAG, "HANDLE ALLOC: OMX_EGL_Render: {} error: {:#x}", static_cast<void*>(componentHandle), error);
+ if (error != OMX_ErrorNone) return false;
+
+ handle_egl_render = componentHandle;
+
+ OMX_PORT_PARAM_TYPE p_param;
+ memset(&p_param, 0, sizeof(p_param));
+ p_param.nSize = sizeof(p_param);
+ p_param.nVersion.nVersion = OMX_VERSION;
+
+ error = OMX_GetParameter(componentHandle, OMX_IndexParamVideoInit, &p_param);
+ if (error != OMX_ErrorNone) return false;
+
+ inputPort = p_param.nStartPortNumber;
+ outputPort = p_param.nStartPortNumber + 1;
+
+ log->debug(TAG, "IN: {}, OUT: {}", inputPort, outputPort);
+ return true;
+}
+
+void OMX_EGL_Render::shutdown()
+{
+ stopEventsProcessing();
+ OMX_ERRORTYPE error = OMX_FreeHandle(componentHandle);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeHandle in OMX_EGL_Render::shutdown()", error);
+}
+
+void OMX_EGL_Render::prepareInputPort(OMX_U32 frameWidth, OMX_U32 frameHeight, OMX_U32 stride)
+{
+ log->debug(TAG, "prepareInputPort");
+
+ // Calculate a slice height. It must be divisible by 16 and >= frameHeight
+ // Temporarily use sliceHeight for remainder
+ int sliceHeight = frameHeight % 16;
+ if (sliceHeight == 0)
+ sliceHeight = frameHeight;
+ else
+ sliceHeight = frameHeight + 16 - sliceHeight;
+
+ // Buffer size IS THE SLICE HEIGHT multiplied by the stride
+ inputPortBufferSize = sliceHeight * stride;
+
+
+ OMX_PARAM_PORTDEFINITIONTYPE portConfig;
+ memset(&portConfig, 0, sizeof(portConfig));
+ portConfig.nSize = sizeof(portConfig);
+ portConfig.nVersion.nVersion = OMX_VERSION;
+ portConfig.nPortIndex = inputPort;
+
+ OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &portConfig);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_EGL_Render::prepareInputPort()", error);
+
+ log->debug(TAG, "Recv from get:");
+ log->debug(TAG, "nPortIndex: {}", portConfig.nPortIndex);
+ log->debug(TAG, "eDir: {}", portConfig.eDir);
+ log->debug(TAG, "buffer count actual: {}", portConfig.nBufferCountActual);
+ log->debug(TAG, "buffer count min: {}", portConfig.nBufferCountMin);
+ log->debug(TAG, "nBufferSize: {}", portConfig.nBufferSize);
+ log->debug(TAG, "bEnabled: {}", portConfig.bEnabled);
+ log->debug(TAG, "bPopulated: {}", portConfig.bPopulated);
+ log->debug(TAG, "eDomain: {}", portConfig.eDomain);
+
+ portConfig.nBufferCountActual = 1;
+ portConfig.nBufferSize = inputPortBufferSize;
+ portConfig.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ portConfig.format.video.eColorFormat = OMX_COLOR_Format32bitABGR8888;
+ portConfig.format.video.nFrameWidth = frameWidth;
+ portConfig.format.video.nStride = stride;
+ portConfig.format.video.nFrameHeight = frameHeight;
+ portConfig.format.video.nSliceHeight = sliceHeight;
+
+ log->debug(TAG, "Setting buf count actual: {}, buf size: {}", portConfig.nBufferCountActual, portConfig.nBufferSize);
+ log->debug(TAG, "calling setParameter: w {} h {} str {} sli {}", portConfig.format.video.nFrameWidth, portConfig.format.video.nFrameHeight,
+ portConfig.format.video.nStride, portConfig.format.video.nSliceHeight);
+
+ error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &portConfig);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_EGL_Render::prepareInputPort()", error);
+
+ printPortSettings(false);
+}
+
+void OMX_EGL_Render::prepareOutputPort(EGLDisplay egldisplay)
+{
+ OMX_PARAM_PORTDEFINITIONTYPE port_def_type;
+ memset(&port_def_type, 0, sizeof(port_def_type));
+ port_def_type.nSize = sizeof(port_def_type);
+ port_def_type.nVersion.nVersion = OMX_VERSION;
+ port_def_type.nPortIndex = outputPort;
+
+ OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_EGL_Render::prep()", error);
+
+ port_def_type.nBufferCountActual = 1;
+ port_def_type.format.video.pNativeWindow = egldisplay;
+
+ error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type); // set port def: buffer count actual = 1
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_EGL_Render::prep()", error);
+}
+
+void OMX_EGL_Render::allocateInputBuffers(char* data)
+{
+ inBuffer1 = NULL;
+
+ log->debug(TAG, "Calling OMX_UseBuffer with data = {}", static_cast<void*>(data));
+ OMX_ERRORTYPE error = OMX_UseBuffer(componentHandle, &inBuffer1, inputPort, static_cast<OMX_PTR>(0), inputPortBufferSize, reinterpret_cast<OMX_U8*>(data));
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer in OMX_EGL_Render::allocateInputBuffers()", error);
+
+ log->debug(TAG, "OMX_UseBuffer:");
+ log->debug(TAG, " nSize = {}", inBuffer1->nSize);
+ log->debug(TAG, " pBuffer = {}", static_cast<void*>(inBuffer1->pBuffer));
+ log->debug(TAG, " nAllocLen = {}", inBuffer1->nAllocLen);
+ log->debug(TAG, " nFilledLen = {}", inBuffer1->nFilledLen);
+ log->debug(TAG, " nOffset = {}", inBuffer1->nOffset);
+ log->debug(TAG, " nInputPortIndex = {}", inBuffer1->nInputPortIndex);
+ log->debug(TAG, " nOutputPortIndex = {}", inBuffer1->nOutputPortIndex);
+ log->debug(TAG, " nFlags = {:#x}", inBuffer1->nFlags);
+
+ printPortSettings(false);
+}
+
+void OMX_EGL_Render::allocateEGLImageKHR(EGLImageKHR eglimagekhr)
+{
+ OMX_ERRORTYPE error = OMX_UseEGLImage(componentHandle, &eglRenderOutputBufferHeader, outputPort, this, static_cast<void*>(eglimagekhr));
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseEGLImage in OMX_EGL_Render::allocateEGLImageKHR()", error);
+
+ printPortSettings(true);
+}
+
+void OMX_EGL_Render::deallocateInputBuffers()
+{
+ OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, inputPort, inBuffer1);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer in OMX_EGL_Render::deallocateInputBuffers()", error);
+
+ inBuffer1 = NULL;
+}
+
+void OMX_EGL_Render::deallocateEGLImageKHR()
+{
+ OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, outputPort, eglRenderOutputBufferHeader);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer in OMX_EGL_Render::deallocateEGLImageKHR()", error);
+
+ eglRenderOutputBufferHeader = NULL;
+}
+
+void OMX_EGL_Render::sendToInput(char* data, int dataSize)
+{
+ log->debug(TAG, "Starting sendToInput {}", dataSize);
+
+ inBuffer1->nFilledLen = dataSize;
+ inBuffer1->nOffset = 0;
+ inBuffer1->nTimeStamp = { 0, 0 };
+ inBuffer1->pAppPrivate = static_cast<OMX_PTR>(0);
+ inBuffer1->nFlags |= OMX_BUFFERFLAG_EOS;
+ inBuffer1->pBuffer = reinterpret_cast<OMX_U8*>(data);
+
+ log->debug(TAG, "calling emptythisbuffer");
+ OMX_ERRORTYPE error = OMX_EmptyThisBuffer(componentHandle, inBuffer1);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_EmptyThisBuffer in OMX_EGL_Render::sendToInput()", error);
+ log->debug(TAG, "sendToInput done");
+}
+
+void OMX_EGL_Render::render()
+{
+ struct BufferWithOutputPort* bufferWithOutputPort{};
+
+ try
+ {
+ bufferWithOutputPort = new BufferWithOutputPort();
+ bufferWithOutputPort->bufhead = eglRenderOutputBufferHeader;
+ eglRenderOutputBufferHeader->pAppPrivate = static_cast<OMX_PTR>(bufferWithOutputPort);
+
+ log->debug(TAG, "render: egl buffer header:");
+ log->debug(TAG, "{}", static_cast<void*>(eglRenderOutputBufferHeader->pBuffer));
+ log->debug(TAG, "{}", eglRenderOutputBufferHeader->nAllocLen);
+ log->debug(TAG, "{}", eglRenderOutputBufferHeader->nFilledLen);
+ log->debug(TAG, "{}", eglRenderOutputBufferHeader->nOffset);
+ log->debug(TAG, "{:#x}", eglRenderOutputBufferHeader->nFlags);
+ log->debug(TAG, "{}", eglRenderOutputBufferHeader->nInputPortIndex);
+ log->debug(TAG, "{}", eglRenderOutputBufferHeader->nOutputPortIndex);
+ log->debug(TAG, "{}", static_cast<void*>(eglRenderOutputBufferHeader->pAppPrivate));
+
+
+ std::unique_lock<std::mutex> ul(bufferWithOutputPort->mutex);
+
+ log->debug(TAG, "calling fillthisbuffer. bufhead: {}, BufferWithOutput: {}", static_cast<void*>(eglRenderOutputBufferHeader), static_cast<void*>(bufferWithOutputPort));
+
+ OMX_ERRORTYPE error = OMX_FillThisBuffer(componentHandle, eglRenderOutputBufferHeader);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FillThisBuffer in OMX_EGL_Render::render()", error);
+
+ log->debug(TAG, "done fillthisbuffer. bufhead: {}, BufferWithOutput: {}", static_cast<void*>(eglRenderOutputBufferHeader), static_cast<void*>(bufferWithOutputPort));
+
+ bufferWithOutputPort->cond.wait(ul, [bufferWithOutputPort] { return bufferWithOutputPort->done; });
+ ul.unlock();
+
+ log->debug(TAG, "receiveFromOutputPort signalled");
+
+ log->debug(TAG, "receiveFromOutput:");
+ log->debug(TAG, " nSize = {}", eglRenderOutputBufferHeader->nSize);
+ log->debug(TAG, " nAllocLen = {}", eglRenderOutputBufferHeader->nAllocLen);
+ log->debug(TAG, " nFilledLen = {}", eglRenderOutputBufferHeader->nFilledLen);
+ log->debug(TAG, " nOffset = {}", eglRenderOutputBufferHeader->nOffset);
+ log->debug(TAG, " nInputPortIndex = {}", eglRenderOutputBufferHeader->nInputPortIndex);
+ log->debug(TAG, " nOutputPortIndex = {}", eglRenderOutputBufferHeader->nOutputPortIndex);
+ log->debug(TAG, " nFlags = {:#x}", eglRenderOutputBufferHeader->nFlags);
+
+ delete bufferWithOutputPort;
+ bufferWithOutputPort = NULL;
+ }
+ catch(...)
+ {
+ if (bufferWithOutputPort)
+ {
+ free(bufferWithOutputPort);
+ bufferWithOutputPort = NULL;
+ }
+ throw;
+ }
+}
+
+void OMX_EGL_Render::printPortSettings(bool which)
+{
+ OMX_PARAM_PORTDEFINITIONTYPE portSettings;
+
+ memset(&portSettings, 0, sizeof(portSettings));
+ portSettings.nSize = sizeof(portSettings);
+ portSettings.nVersion.nVersion = OMX_VERSION;
+
+ if (which)
+ {
+ log->debug(TAG, "------ Port settings for OUTPUT:");
+ portSettings.nPortIndex = outputPort;
+ }
+ else
+ {
+ log->debug(TAG, "------ Port settings for INPUT:");
+ portSettings.nPortIndex = inputPort;
+ }
+
+ OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &portSettings);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_EGL_Render::printPortSettings()", error);
+
+ log->debug(TAG, " nPortIndex: {}", portSettings.nPortIndex);
+ log->debug(TAG, " buffer count min, actual, size: {} {} {}", portSettings.nBufferCountMin, portSettings.nBufferCountActual, portSettings.nBufferSize);
+ log->debug(TAG, " eDir: {}", portSettings.eDir);
+ log->debug(TAG, " bEnabled: {}", portSettings.bEnabled);
+ log->debug(TAG, " bPopulated: {}", portSettings.bPopulated);
+ log->debug(TAG, " eDomain: {}", portSettings.eDomain);
+
+ log->debug(TAG, " cMIMEType: {}", static_cast<void*>(portSettings.format.video.cMIMEType));
+ log->debug(TAG, " pNativeRender: {}", static_cast<void*>(portSettings.format.video.pNativeRender));
+ log->debug(TAG, " nFrameWidth: {}", portSettings.format.video.nFrameWidth);
+ log->debug(TAG, " nFrameHeight: {}", portSettings.format.video.nFrameHeight);
+ log->debug(TAG, " nStride: {}", portSettings.format.video.nStride);
+ log->debug(TAG, " nSliceHeight: {}", portSettings.format.video.nSliceHeight);
+ log->debug(TAG, " nBitrate: {}", portSettings.format.video.nBitrate);
+ log->debug(TAG, " xFramerate: {}", portSettings.format.video.xFramerate);
+ log->debug(TAG, " eCompressionFormat: {:#x}", portSettings.format.video.eCompressionFormat);
+ log->debug(TAG, " eColorFormat: {:#x}", portSettings.format.video.eColorFormat);
+}
+
+OMX_ERRORTYPE OMX_EGL_Render::cb_EmptyBufferDone(OMX_IN OMX_HANDLETYPE /*hcomp*/, OMX_IN OMX_PTR /*appdata*/, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
+{
+ log->debug(TAG, "CB: EmptyBufferDone");
+ log->debug(TAG, " nSize = {}", buffer->nSize);
+ log->debug(TAG, " nAllocLen = {}", buffer->nAllocLen);
+ log->debug(TAG, " nFilledLen = {}", buffer->nFilledLen);
+ log->debug(TAG, " nOffset = {}", buffer->nOffset);
+ log->debug(TAG, " nInputPortIndex = {}", buffer->nInputPortIndex);
+ log->debug(TAG, " nOutputPortIndex = {}", buffer->nOutputPortIndex);
+ log->debug(TAG, " nFlags = {:#x}", buffer->nFlags);
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE OMX_EGL_Render::cb_FillBufferDone(OMX_IN OMX_HANDLETYPE hcomp, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
+{
+ log->debug(TAG, "CB: FillBufferDone, handle: {}, appdata: {}, buffer: {}", static_cast<void*>(hcomp), static_cast<void*>(appdata), static_cast<void*>(buffer));
+
+ struct BufferWithOutputPort* bufferWithOutputPort = static_cast<struct BufferWithOutputPort*>(buffer->pAppPrivate);
+ bufferWithOutputPort->mutex.lock();
+ bufferWithOutputPort->done = true;
+ bufferWithOutputPort->mutex.unlock();
+ bufferWithOutputPort->cond.notify_one();
+
+ return OMX_ErrorNone;
+}
--- /dev/null
+/*
+ Copyright 2021 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef OMXEGLRENDER_H
+#define OMXEGLRENDER_H
+
+#include <VG/openvg.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "omx.h"
+
+class OMX_EGL_Render : public OMX
+{
+ friend class OMX;
+
+ public:
+ OMX_EGL_Render();
+ virtual ~OMX_EGL_Render();
+
+ bool init();
+ void shutdown();
+
+ void prepareInputPort(OMX_U32 frameWidth, OMX_U32 frameHeight, OMX_U32 stride); // throws
+ void prepareOutputPort(EGLDisplay egldisplay); // throws
+
+ void allocateInputBuffers(char* buffer); // throws
+ void allocateEGLImageKHR(EGLImageKHR eglimagekhr); // throws
+
+ void deallocateInputBuffers(); // throws
+ void deallocateEGLImageKHR(); // throws
+
+ void sendToInput(char* data, int dataSize); // throws
+ void render(); // throws
+
+ void printPortSettings(bool which); // throws
+
+ // Buffers callback overrides
+
+ OMX_ERRORTYPE cb_FillBufferDone(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer) override;
+
+ OMX_ERRORTYPE cb_EmptyBufferDone(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer) override;
+
+ void enableInput(bool wait = true) { enablePort(inputPort, true, wait); } // throws
+ void disableInput(bool wait = true) { enablePort(inputPort, false, wait); } // throws
+ void enableOutput(bool wait = true) { enablePort(outputPort, true, wait); } // throws
+ void disableOutput(bool wait = true) { enablePort(outputPort, false, wait); } // throws
+ void flushInputCommands(bool wait = true) { flushCommands(inputPort, wait); } // throws
+ void flushOutputCommands(bool wait = true) { flushCommands(outputPort, wait); } // throws
+
+ protected:
+
+ OMX_U32 inputPort;
+ OMX_U32 outputPort;
+
+ OMX_BUFFERHEADERTYPE* inBuffer1 = NULL;
+ OMX_BUFFERHEADERTYPE* eglRenderOutputBufferHeader;
+
+ int inputPortBufferSize{};
+};
+
+#endif
--- /dev/null
+/*
+ Copyright 2021 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "../log.h"
+
+#include "omximagedecode.h"
+
+static const char* TAG = "OMX_Image_Decode";
+
+OMX_Image_Decode::OMX_Image_Decode()
+{
+ omx_image_decode = this;
+}
+
+OMX_Image_Decode::~OMX_Image_Decode()
+{
+ omx_image_decode = NULL;
+ handle_image_decode = 0;
+}
+
+bool OMX_Image_Decode::init()
+{
+ initEventsProcessing();
+
+ OMX_CALLBACKTYPE callbacks = {&scb_EventHandler, &scb_EmptyBufferDone, &scb_FillBufferDone};
+
+ char* componentName;
+ asprintf(&componentName, "%s", componentName_image_decode);
+ OMX_ERRORTYPE error = OMX_GetHandle(&componentHandle, componentName, NULL, &callbacks);
+ free(componentName);
+ log->debug(TAG, "HANDLE ALLOC: omx_image_decode: {}", static_cast<void*>(componentHandle));
+ if (error != OMX_ErrorNone) return false;
+
+ handle_image_decode = componentHandle;
+
+ OMX_PORT_PARAM_TYPE p_param;
+ memset(&p_param, 0, sizeof(p_param));
+ p_param.nSize = sizeof(p_param);
+ p_param.nVersion.nVersion = OMX_VERSION;
+
+ error = OMX_GetParameter(componentHandle, OMX_IndexParamImageInit, &p_param);
+ if (error != OMX_ErrorNone) return false;
+
+ inputPort = p_param.nStartPortNumber;
+ outputPort = p_param.nStartPortNumber + 1;
+
+ log->debug(TAG, "IN: {}, OUT: {}", inputPort, outputPort);
+ return true;
+}
+
+void OMX_Image_Decode::shutdown()
+{
+ stopEventsProcessing();
+ OMX_ERRORTYPE error = OMX_FreeHandle(componentHandle);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeHandle in OMX_Image_Decode::shutdown()", error);
+}
+
+void OMX_Image_Decode::prepareInputBuffers(int tdataSize)
+{
+ dataSize = tdataSize;
+
+ OMX_PARAM_PORTDEFINITIONTYPE port_def_type;
+ memset(&port_def_type, 0, sizeof(port_def_type));
+ port_def_type.nSize = sizeof(port_def_type);
+ port_def_type.nVersion.nVersion = OMX_VERSION;
+ port_def_type.nPortIndex = inputPort;
+
+ OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_Image_Decode::prepareInputBuffers()", error);
+
+ log->debug(TAG, "Defaults: bufferCountActual: {}, bufferCountMin: {}, nBufferSize: {}",
+ port_def_type.nBufferCountActual, port_def_type.nBufferCountMin, port_def_type.nBufferSize);
+
+ port_def_type.nBufferCountActual = port_def_type.nBufferCountMin;
+ if (dataSize > port_def_type.nBufferSize) port_def_type.nBufferSize = dataSize;
+
+ log->debug(TAG, "Setting buf count actual: {}, buf size: {}",
+ port_def_type.nBufferCountActual, port_def_type.nBufferSize);
+
+ error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_Image_Decode::prepareInputBuffers()", error);
+}
+
+void OMX_Image_Decode::setFormat()
+{
+ OMX_IMAGE_PARAM_PORTFORMATTYPE ft_type;
+ memset(&ft_type, 0, sizeof(ft_type));
+ ft_type.nSize = sizeof(ft_type);
+ ft_type.nVersion.nVersion = OMX_VERSION;
+ ft_type.eCompressionFormat = OMX_IMAGE_CodingPNG;
+ ft_type.nPortIndex = inputPort;
+
+ // Would use this function to select between PNG and JPEG but image_decode
+ // only outputs JPEG decoded images in YUV420, because of course it does.
+ // ft_type.eCompressionFormat = OMX_IMAGE_CodingJPEG;
+
+ OMX_ERRORTYPE error = OMX_SetParameter(componentHandle, OMX_IndexParamImagePortFormat, &ft_type);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_Image_Decode::setFormat()", error);
+ log->debug(TAG, "setFormat OK");
+}
+
+void OMX_Image_Decode::setSliceHeight(int newSliceHeight)
+{
+ outputPortSettings.format.image.nSliceHeight = newSliceHeight;
+ log->debug(TAG, "nSliceHeight: {}", outputPortSettings.format.image.nSliceHeight);
+
+ OMX_ERRORTYPE error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &outputPortSettings);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_Image_Decode::setSliceHeight()", error);
+
+ {
+ // Dump new independant output settings
+ OMX_PARAM_PORTDEFINITIONTYPE indTest;
+ memset(&indTest, 0, sizeof(indTest));
+ indTest.nSize = sizeof(indTest);
+ indTest.nVersion.nVersion = OMX_VERSION;
+ indTest.nPortIndex = outputPort;
+
+ error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &indTest);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_Image_Decode::setSliceHeight()", error);
+
+ log->debug(TAG, "Port def type from outputPort");
+ log->debug(TAG, "buffer count actual: {}", indTest.nBufferCountActual);
+ log->debug(TAG, "eDir: {}", indTest.eDir);
+ log->debug(TAG, "nBufferSize: {}", indTest.nBufferSize);
+ log->debug(TAG, "bEnabled: {}", indTest.bEnabled);
+ log->debug(TAG, "bPopulated: {}", indTest.bPopulated);
+ log->debug(TAG, "eDomain: {}", indTest.eDomain);
+
+ log->debug(TAG, "cMIMEType: {}", static_cast<void*>(indTest.format.image.cMIMEType));
+ log->debug(TAG, "pNativeRender: {}", static_cast<void*>(indTest.format.image.pNativeRender));
+ log->debug(TAG, "nFrameWidth: {}", indTest.format.image.nFrameWidth);
+ log->debug(TAG, "nFrameHeight: {}", indTest.format.image.nFrameHeight);
+ log->debug(TAG, "nStride: {}", indTest.format.image.nStride);
+ log->debug(TAG, "nSliceHeight: {}", indTest.format.image.nSliceHeight);
+ log->debug(TAG, "eColorFormat: {:#x}", indTest.format.image.eColorFormat);
+ }
+}
+
+void OMX_Image_Decode::allocateInputBuffers(char* data)
+{
+ inBuffer1 = NULL;
+ inBuffer2 = NULL;
+
+ log->debug(TAG, "Calling OMX_UseBuffer");
+ OMX_ERRORTYPE error = OMX_UseBuffer(componentHandle, &inBuffer1, inputPort, static_cast<OMX_PTR>(0), dataSize, reinterpret_cast<OMX_U8*>(data));
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer 1 in OMX_Image_Decode::allocateInputBuffers()", error);
+
+ // Hardcoded assumption - image_decode has min 2 input buffers
+ error = OMX_UseBuffer(componentHandle, &inBuffer2, inputPort, static_cast<OMX_PTR>(0), 0, NULL);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer 2 in OMX_Image_Decode::allocateInputBuffers()", error);
+}
+
+void OMX_Image_Decode::allocateOutputBuffer()
+{
+ OMX_ERRORTYPE error = OMX_UseBuffer(componentHandle, &outputBufferHeader, outputPort, static_cast<OMX_PTR>(0), outputPortSettings.nBufferSize, (OMX_U8*)NULL);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer in OMX_Image_Decode::allocateOutputBuffer()", error);
+}
+
+void OMX_Image_Decode::deallocateInputBuffers()
+{
+ OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, inputPort, inBuffer1);
+ inBuffer1 = NULL;
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer 1 in OMX_Image_Decode::deallocateInputBuffers()", error);
+
+ error = OMX_FreeBuffer(componentHandle, inputPort, inBuffer2);
+ inBuffer2 = NULL;
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer 2 in OMX_Image_Decode::deallocateInputBuffers()", error);
+}
+
+void OMX_Image_Decode::deallocateOutputBuffer()
+{
+ OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, outputPort, outputBufferHeader);
+ outputBufferHeader = NULL;
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer in OMX_Image_Decode::deallocateOutputBuffer()", error);
+}
+
+void OMX_Image_Decode::sendToInput()
+{
+ log->debug(TAG, "Starting sendToInput");
+ inBuffer1->nFilledLen = dataSize;
+ inBuffer1->nOffset = 0;
+ inBuffer1->nTimeStamp = { 0, 0 };
+ inBuffer1->pAppPrivate = static_cast<OMX_PTR>(0);
+ inBuffer1->nFlags |= OMX_BUFFERFLAG_EOS;
+
+ log->debug(TAG, "calling emptythisbuffer");
+ OMX_ERRORTYPE error = OMX_EmptyThisBuffer(componentHandle, inBuffer1);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_EmptyThisBuffer in OMX_Image_Decode::sendToInput()", error);
+ log->debug(TAG, "sendToInput done");
+}
+
+void OMX_Image_Decode::waitForOutputPortSettingsChange()
+{
+ eventsProcessorMutex.lock();
+ std::unique_lock<std::mutex> ul(eventWaitersMutex);
+
+ EventWaiter* eventWaiter = new EventWaiter();
+ eventWaiter->waiting = true;
+ eventWaiter->eventType = OMX_EventPortSettingsChanged;
+ eventWaiter->port = outputPort;
+ eventWaiters.push_back(eventWaiter);
+
+ eventsProcessorWake = true;
+ eventsProcessorMutex.unlock();
+ eventsProcessorCond.notify_one();
+
+ eventWaiter->cond.wait(ul); // sleep this thread
+ ul.unlock();
+
+ if (!eventWaiter->receivedEvent)
+ {
+ delete eventWaiter;
+ throw OMX_Exception("Error waiting for event in waitForOutputPortSettingsChange", 0);
+ }
+
+ // The event processor thread received an event and saved it here
+ log->debug(TAG, "received outputportsettingschanged event!");
+ delete eventWaiter->receivedEvent;
+ delete eventWaiter;
+
+ memset(&outputPortSettings, 0, sizeof(outputPortSettings));
+ outputPortSettings.nSize = sizeof(outputPortSettings);
+ outputPortSettings.nVersion.nVersion = OMX_VERSION;
+ outputPortSettings.nPortIndex = outputPort;
+
+ OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &outputPortSettings);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_Image_Decode::waitForOutputPortSettingsChange()", error);
+
+ log->debug(TAG, "Port def type from outputPort");
+ log->debug(TAG, "buffer count actual: {}", outputPortSettings.nBufferCountActual);
+ log->debug(TAG, "eDir: {}", outputPortSettings.eDir);
+ log->debug(TAG, "nBufferSize: {}", outputPortSettings.nBufferSize);
+ log->debug(TAG, "bEnabled: {}", outputPortSettings.bEnabled);
+ log->debug(TAG, "bPopulated: {}", outputPortSettings.bPopulated);
+ log->debug(TAG, "eDomain: {}", outputPortSettings.eDomain);
+
+ log->debug(TAG, "cMIMEType: {}", static_cast<void*>(outputPortSettings.format.image.cMIMEType));
+ log->debug(TAG, "pNativeRender: {}", static_cast<void*>(outputPortSettings.format.image.pNativeRender));
+ log->debug(TAG, "nFrameWidth: {}", outputPortSettings.format.image.nFrameWidth);
+ log->debug(TAG, "nFrameHeight: {}", outputPortSettings.format.image.nFrameHeight);
+ log->debug(TAG, "nStride: {}", outputPortSettings.format.image.nStride);
+ log->debug(TAG, "nSliceHeight: {}", outputPortSettings.format.image.nSliceHeight);
+}
+
+void OMX_Image_Decode::receiveFromOutput(char** data, int* nFlags)
+{
+ void* outputBufferMem{};
+
+ try
+ {
+ outputBufferMem = malloc(outputPortSettings.nBufferSize);
+ memset(outputBufferMem, 0, outputPortSettings.nBufferSize);
+ outputBufferHeader->pBuffer = static_cast<OMX_U8*>(outputBufferMem);
+
+ struct BufferWithOutputPort* bufferWithOutputPort = new BufferWithOutputPort();
+ bufferWithOutputPort->bufhead = outputBufferHeader;
+ outputBufferHeader->pAppPrivate = static_cast<OMX_PTR>(bufferWithOutputPort);
+
+ std::unique_lock<std::mutex> ul(bufferWithOutputPort->mutex);
+
+ log->debug(TAG, "calling fillthisbuffer. bufhead: {}, BufferWithOutput: {}", static_cast<void*>(outputBufferHeader), static_cast<void*>(bufferWithOutputPort));
+
+ OMX_ERRORTYPE error;
+
+ error = OMX_FillThisBuffer(componentHandle, outputBufferHeader);
+ if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FillThisBuffer in OMX_Image_Decode::receiveFromOutput()", error);
+
+ log->debug(TAG, "called fillthisbuffer. bufsize = {}", outputPortSettings.nBufferSize);
+
+ bufferWithOutputPort->cond.wait(ul, [bufferWithOutputPort] { return bufferWithOutputPort->done; });
+ ul.unlock();
+
+ log->debug(TAG, "receiveFromOutputPort signalled");
+
+ *data = static_cast<char*>(outputBufferMem); // caller takes ownership
+ *nFlags = outputBufferHeader->nFlags;
+
+ outputBufferMem = NULL;
+
+ // The slice height is all important - can't trust nFilledLen
+
+ log->debug(TAG, "receiveFromOutput:");
+ log->debug(TAG, " nSize = {}", outputBufferHeader->nSize);
+ log->debug(TAG, " nAllocLen = {}", outputBufferHeader->nAllocLen);
+ log->debug(TAG, " nFilledLen = {}", outputBufferHeader->nFilledLen);
+ log->debug(TAG, " nOffset = {}", outputBufferHeader->nOffset);
+ log->debug(TAG, " nInputPortIndex = {}", outputBufferHeader->nInputPortIndex);
+ log->debug(TAG, " nOutputPortIndex = {}", outputBufferHeader->nOutputPortIndex);
+ log->debug(TAG, " nFlags = {:#x}", outputBufferHeader->nFlags);
+
+ delete bufferWithOutputPort;
+ }
+ catch(...)
+ {
+ if (outputBufferMem)
+ {
+ free(outputBufferMem);
+ outputBufferMem = NULL;
+ }
+ throw;
+ }
+}
+
+void OMX_Image_Decode::getImageInfo(int* width, int* height, int* stride, int* sliceHeight)
+{
+ *width = outputPortSettings.format.image.nFrameWidth;
+ *height = outputPortSettings.format.image.nFrameHeight;
+ *stride = outputPortSettings.format.image.nStride;
+ *sliceHeight = outputPortSettings.format.image.nSliceHeight;
+}
+
+OMX_ERRORTYPE OMX_Image_Decode::cb_EmptyBufferDone(OMX_IN OMX_HANDLETYPE /*hcomp*/, OMX_IN OMX_PTR /*appdata*/, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
+{
+ log->debug(TAG, "CB: EmptyBufferDone");
+ log->debug(TAG, " nSize = {}", buffer->nSize);
+ log->debug(TAG, " nAllocLen = {}", buffer->nAllocLen);
+ log->debug(TAG, " nFilledLen = {}", buffer->nFilledLen);
+ log->debug(TAG, " nOffset = {}", buffer->nOffset);
+ log->debug(TAG, " nInputPortIndex = {}", buffer->nInputPortIndex);
+ log->debug(TAG, " nOutputPortIndex = {}", buffer->nOutputPortIndex);
+ log->debug(TAG, " nFlags = {:#x}", buffer->nFlags);
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE OMX_Image_Decode::cb_FillBufferDone(OMX_IN OMX_HANDLETYPE hcomp, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
+{
+ log->debug(TAG, "CB: FillBufferDone, handle: {}, appdata: {}, buffer: {}", static_cast<void*>(hcomp), static_cast<void*>(appdata), static_cast<void*>(buffer));
+
+ struct BufferWithOutputPort* bufferWithOutputPort = static_cast<struct BufferWithOutputPort*>(buffer->pAppPrivate);
+ bufferWithOutputPort->mutex.lock();
+ bufferWithOutputPort->done = true;
+ bufferWithOutputPort->mutex.unlock();
+ bufferWithOutputPort->cond.notify_one();
+
+ return OMX_ErrorNone;
+}
--- /dev/null
+/*
+ Copyright 2021 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef OMXIMAGEDECODE_H
+#define OMXIMAGEDECODE_H
+
+#include "omx.h"
+
+class OMX_Image_Decode : public OMX
+{
+ friend class OMX;
+
+ public:
+ OMX_Image_Decode();
+ virtual ~OMX_Image_Decode();
+
+ bool init();
+ void shutdown();
+
+ void prepareInputBuffers(int dataSize); // throws
+ void setFormat(); // throws
+ void setSliceHeight(int sliceHeight); // throws
+
+ void allocateInputBuffers(char* data); // throws
+ void allocateOutputBuffer();
+
+ void deallocateInputBuffers(); // throws
+ void deallocateOutputBuffer(); // throws
+
+ void sendToInput(); // throws
+
+ void waitForOutputPortSettingsChange(); // throws
+ void receiveFromOutput(char** data, int* nFlags); // throws // caller frees data
+ void getImageInfo(int* width, int* height, int* stride, int* sliceHeight);
+
+ void enableInput(bool wait = true) { enablePort(inputPort, true, wait); } // throws
+ void disableInput(bool wait = true) { enablePort(inputPort, false, wait); } // throws
+ void enableOutput(bool wait = true) { enablePort(outputPort, true, wait); } // throws
+ void disableOutput(bool wait = true) { enablePort(outputPort, false, wait); } // throws
+ void flushInputCommands(bool wait = true) { flushCommands(inputPort, wait); }
+ void flushOutputCommands(bool wait = true) { flushCommands(outputPort, wait); }
+
+ // Buffers callback overrides
+
+ OMX_ERRORTYPE cb_FillBufferDone(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer) override;
+
+ OMX_ERRORTYPE cb_EmptyBufferDone(
+ OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer) override;
+
+ protected:
+
+ OMX_U32 inputPort;
+ OMX_U32 outputPort;
+ OMX_BUFFERHEADERTYPE* inBuffer1{};
+ OMX_BUFFERHEADERTYPE* inBuffer2{};
+
+ unsigned int dataSize{};
+
+ OMX_PARAM_PORTDEFINITIONTYPE outputPortSettings;
+
+ OMX_BUFFERHEADERTYPE* outputBufferHeader{};
+};
+
+#endif
vgmutex.unlock();
#ifdef PICTURE_DECODER_OMX
- imageomx = new ImageOMX(&reader);
- reader.addDecoder(imageomx);
+ // imageomx = new ImageOMX(&reader);
+ // reader.addDecoder(imageomx);
+
+ imageomx2 = new ImageOMX2(/*egl_display*/);
+ reader.addDecoder(imageomx2);
#endif
return 1;
reader.shutdown();
#ifdef PICTURE_DECODER_OMX
- if (imageomx) reader.removeDecoder(imageomx);
- imageomx = NULL;
+// if (imageomx) reader.removeDecoder(imageomx);
+// imageomx = NULL;
+ if (imageomx2) reader.removeDecoder(imageomx2);
+ imageomx2 = NULL;
#endif
if (!initted) return 0;
return;
}
- if (renderThreadReq == 0) abort(); // OSDOVG-ROD-EXPERIMENT - check this never happens
+ if (renderThreadReq == 0) abort(); // OSDOVG-ROD-EXPERIMENT - check this never happens - confirmed
// Either timeout or renderThreadReq == 1 - go around
#include "videoomx.h"
#include "staticartwork.h"
#ifdef PICTURE_DECODER_OMX
- #include "imageomx.h"
+ //#include "imageomx.h"
+ #include "imageomx2.h"
#endif
#include <ft2build.h>
struct OpenVGResponse* response{}; // If !NULL, a thread is waiting
};
+class ImageOMX2;
+
+#include "eglpicturecreator.h"
+
+
class OsdOpenVG : public OsdVector
#ifdef PICTURE_DECODER_OMX
, public EGLPictureCreator
uint8_t* static_artwork_end[sa_MAX];
#ifdef PICTURE_DECODER_OMX
- ImageOMX* imageomx;
+ //ImageOMX* imageomx;
+ ImageOMX2* imageomx2;
#endif
};
}
surfaces_mutex.unlock();
+
+ // OSDOVG-ROD-EXPERIMENT
+ logger->trace(TAG, "EXPERIMENT - call doRender");
+ doRender();
}
void OsdVector::processMessage(Message* m)
class PictureDecoder
{
public:
- PictureDecoder(PictureReader* treader) {reader = treader;};
+ PictureDecoder(PictureReader* treader) {/* reader = treader; */};
virtual ~PictureDecoder() {};
// its is always guaranted, that after getDecodedPicture a call to decodePicture follows, if the return value was true;
virtual void shutdown() {};
protected:
- PictureReader* reader;
+ //PictureReader* reader;
};
class PictureReader
void VChannelList::draw()
{
TBBoxx::draw();
- sl.draw();
// Put the status stuff at the bottom