2 Copyright 2021 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP. If not, see <https://www.gnu.org/licenses/>.
20 #include <VG/openvg.h>
22 #include <EGL/eglext.h>
26 #include "omxeglrender.h"
28 static const char* TAG = "OMX_EGL_Render";
31 OMX_EGL_Render::OMX_EGL_Render()
33 omx_egl_render = this;
36 OMX_EGL_Render::~OMX_EGL_Render()
38 omx_image_decode = NULL;
39 handle_egl_render = 0;
42 bool OMX_EGL_Render::init()
44 initEventsProcessing();
46 OMX_CALLBACKTYPE callbacks = {&scb_EventHandler, &scb_EmptyBufferDone, &scb_FillBufferDone};
49 asprintf(&componentName, "%s", componentName_egl_render);
50 OMX_ERRORTYPE error = OMX_GetHandle(&componentHandle, componentName, NULL, &callbacks);
52 // log->debug(TAG, "HANDLE ALLOC: OMX_EGL_Render: {} error: {:#x}", static_cast<void*>(componentHandle), error);
53 if (error != OMX_ErrorNone) return false;
55 handle_egl_render = componentHandle;
57 OMX_PORT_PARAM_TYPE p_param;
58 memset(&p_param, 0, sizeof(p_param));
59 p_param.nSize = sizeof(p_param);
60 p_param.nVersion.nVersion = OMX_VERSION;
62 error = OMX_GetParameter(componentHandle, OMX_IndexParamVideoInit, &p_param);
63 if (error != OMX_ErrorNone) return false;
65 inputPort = p_param.nStartPortNumber;
66 outputPort = p_param.nStartPortNumber + 1;
68 // log->debug(TAG, "IN: {}, OUT: {}", inputPort, outputPort);
72 void OMX_EGL_Render::shutdown()
74 stopEventsProcessing();
75 OMX_ERRORTYPE error = OMX_FreeHandle(componentHandle);
76 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeHandle in OMX_EGL_Render::shutdown()", error);
79 void OMX_EGL_Render::prepareInputPort(OMX_U32 frameWidth, OMX_U32 frameHeight, OMX_U32 stride)
81 // log->debug(TAG, "prepareInputPort");
83 // Calculate a slice height. It must be divisible by 16 and >= frameHeight
84 // Temporarily use sliceHeight for remainder
85 int sliceHeight = frameHeight % 16;
87 sliceHeight = frameHeight;
89 sliceHeight = frameHeight + 16 - sliceHeight;
91 // Buffer size IS THE SLICE HEIGHT multiplied by the stride
92 inputPortBufferSize = sliceHeight * stride;
95 OMX_PARAM_PORTDEFINITIONTYPE portConfig;
96 memset(&portConfig, 0, sizeof(portConfig));
97 portConfig.nSize = sizeof(portConfig);
98 portConfig.nVersion.nVersion = OMX_VERSION;
99 portConfig.nPortIndex = inputPort;
101 OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &portConfig);
102 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_EGL_Render::prepareInputPort()", error);
105 log->debug(TAG, "Recv from get:");
106 log->debug(TAG, "nPortIndex: {}", portConfig.nPortIndex);
107 log->debug(TAG, "eDir: {}", portConfig.eDir);
108 log->debug(TAG, "buffer count actual: {}", portConfig.nBufferCountActual);
109 log->debug(TAG, "buffer count min: {}", portConfig.nBufferCountMin);
110 log->debug(TAG, "nBufferSize: {}", portConfig.nBufferSize);
111 log->debug(TAG, "bEnabled: {}", portConfig.bEnabled);
112 log->debug(TAG, "bPopulated: {}", portConfig.bPopulated);
113 log->debug(TAG, "eDomain: {}", portConfig.eDomain);
116 portConfig.nBufferCountActual = 1;
117 portConfig.nBufferSize = inputPortBufferSize;
118 portConfig.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
119 portConfig.format.video.eColorFormat = OMX_COLOR_Format32bitABGR8888;
120 portConfig.format.video.nFrameWidth = frameWidth;
121 portConfig.format.video.nStride = stride;
122 portConfig.format.video.nFrameHeight = frameHeight;
123 portConfig.format.video.nSliceHeight = sliceHeight;
125 // log->debug(TAG, "Setting buf count actual: {}, buf size: {}", portConfig.nBufferCountActual, portConfig.nBufferSize);
126 // log->debug(TAG, "calling setParameter: w {} h {} str {} sli {}", portConfig.format.video.nFrameWidth, portConfig.format.video.nFrameHeight,
127 // portConfig.format.video.nStride, portConfig.format.video.nSliceHeight);
129 error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &portConfig);
130 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_EGL_Render::prepareInputPort()", error);
132 // printPortSettings(false);
135 void OMX_EGL_Render::prepareOutputPort(EGLDisplay egldisplay)
137 OMX_PARAM_PORTDEFINITIONTYPE port_def_type;
138 memset(&port_def_type, 0, sizeof(port_def_type));
139 port_def_type.nSize = sizeof(port_def_type);
140 port_def_type.nVersion.nVersion = OMX_VERSION;
141 port_def_type.nPortIndex = outputPort;
143 OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type);
144 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_EGL_Render::prep()", error);
146 port_def_type.nBufferCountActual = 1;
147 port_def_type.format.video.pNativeWindow = egldisplay;
149 error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type); // set port def: buffer count actual = 1
150 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_EGL_Render::prep()", error);
153 void OMX_EGL_Render::allocateInputBuffers(char* data)
157 // log->debug(TAG, "Calling OMX_UseBuffer with data = {}", static_cast<void*>(data));
158 OMX_ERRORTYPE error = OMX_UseBuffer(componentHandle, &inBuffer1, inputPort, static_cast<OMX_PTR>(0), inputPortBufferSize, reinterpret_cast<OMX_U8*>(data));
159 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer in OMX_EGL_Render::allocateInputBuffers()", error);
162 log->debug(TAG, "OMX_UseBuffer:");
163 log->debug(TAG, " nSize = {}", inBuffer1->nSize);
164 log->debug(TAG, " pBuffer = {}", static_cast<void*>(inBuffer1->pBuffer));
165 log->debug(TAG, " nAllocLen = {}", inBuffer1->nAllocLen);
166 log->debug(TAG, " nFilledLen = {}", inBuffer1->nFilledLen);
167 log->debug(TAG, " nOffset = {}", inBuffer1->nOffset);
168 log->debug(TAG, " nInputPortIndex = {}", inBuffer1->nInputPortIndex);
169 log->debug(TAG, " nOutputPortIndex = {}", inBuffer1->nOutputPortIndex);
170 log->debug(TAG, " nFlags = {:#x}", inBuffer1->nFlags);
172 printPortSettings(false);
176 void OMX_EGL_Render::allocateEGLImageKHR(EGLImageKHR eglimagekhr)
178 OMX_ERRORTYPE error = OMX_UseEGLImage(componentHandle, &eglRenderOutputBufferHeader, outputPort, this, static_cast<void*>(eglimagekhr));
179 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseEGLImage in OMX_EGL_Render::allocateEGLImageKHR()", error);
181 // printPortSettings(true);
184 void OMX_EGL_Render::deallocateInputBuffers()
186 OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, inputPort, inBuffer1);
187 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer in OMX_EGL_Render::deallocateInputBuffers()", error);
192 void OMX_EGL_Render::deallocateEGLImageKHR()
194 OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, outputPort, eglRenderOutputBufferHeader);
195 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer in OMX_EGL_Render::deallocateEGLImageKHR()", error);
197 eglRenderOutputBufferHeader = NULL;
200 void OMX_EGL_Render::sendToInput(char* data, int dataSize)
202 // log->debug(TAG, "Starting sendToInput {}", dataSize);
204 inBuffer1->nFilledLen = dataSize;
205 inBuffer1->nOffset = 0;
206 inBuffer1->nTimeStamp = { 0, 0 };
207 inBuffer1->pAppPrivate = static_cast<OMX_PTR>(0);
208 inBuffer1->nFlags |= OMX_BUFFERFLAG_EOS;
209 inBuffer1->pBuffer = reinterpret_cast<OMX_U8*>(data);
211 // log->debug(TAG, "calling emptythisbuffer");
212 OMX_ERRORTYPE error = OMX_EmptyThisBuffer(componentHandle, inBuffer1);
213 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_EmptyThisBuffer in OMX_EGL_Render::sendToInput()", error);
214 // log->debug(TAG, "sendToInput done");
217 void OMX_EGL_Render::render()
219 struct BufferWithOutputPort* bufferWithOutputPort{};
223 bufferWithOutputPort = new BufferWithOutputPort();
224 bufferWithOutputPort->bufhead = eglRenderOutputBufferHeader;
225 eglRenderOutputBufferHeader->pAppPrivate = static_cast<OMX_PTR>(bufferWithOutputPort);
228 log->debug(TAG, "render: egl buffer header:");
229 log->debug(TAG, "{}", static_cast<void*>(eglRenderOutputBufferHeader->pBuffer));
230 log->debug(TAG, "{}", eglRenderOutputBufferHeader->nAllocLen);
231 log->debug(TAG, "{}", eglRenderOutputBufferHeader->nFilledLen);
232 log->debug(TAG, "{}", eglRenderOutputBufferHeader->nOffset);
233 log->debug(TAG, "{:#x}", eglRenderOutputBufferHeader->nFlags);
234 log->debug(TAG, "{}", eglRenderOutputBufferHeader->nInputPortIndex);
235 log->debug(TAG, "{}", eglRenderOutputBufferHeader->nOutputPortIndex);
236 log->debug(TAG, "{}", static_cast<void*>(eglRenderOutputBufferHeader->pAppPrivate));
239 std::unique_lock<std::mutex> ul(bufferWithOutputPort->mutex);
241 // log->debug(TAG, "calling fillthisbuffer. bufhead: {}, BufferWithOutput: {}", static_cast<void*>(eglRenderOutputBufferHeader), static_cast<void*>(bufferWithOutputPort));
243 OMX_ERRORTYPE error = OMX_FillThisBuffer(componentHandle, eglRenderOutputBufferHeader);
244 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FillThisBuffer in OMX_EGL_Render::render()", error);
246 // log->debug(TAG, "done fillthisbuffer. bufhead: {}, BufferWithOutput: {}", static_cast<void*>(eglRenderOutputBufferHeader), static_cast<void*>(bufferWithOutputPort));
248 bufferWithOutputPort->cond.wait(ul, [bufferWithOutputPort] { return bufferWithOutputPort->done; });
252 log->debug(TAG, "receiveFromOutputPort signalled");
254 log->debug(TAG, "receiveFromOutput:");
255 log->debug(TAG, " nSize = {}", eglRenderOutputBufferHeader->nSize);
256 log->debug(TAG, " nAllocLen = {}", eglRenderOutputBufferHeader->nAllocLen);
257 log->debug(TAG, " nFilledLen = {}", eglRenderOutputBufferHeader->nFilledLen);
258 log->debug(TAG, " nOffset = {}", eglRenderOutputBufferHeader->nOffset);
259 log->debug(TAG, " nInputPortIndex = {}", eglRenderOutputBufferHeader->nInputPortIndex);
260 log->debug(TAG, " nOutputPortIndex = {}", eglRenderOutputBufferHeader->nOutputPortIndex);
261 log->debug(TAG, " nFlags = {:#x}", eglRenderOutputBufferHeader->nFlags);
264 delete bufferWithOutputPort;
265 bufferWithOutputPort = NULL;
269 if (bufferWithOutputPort)
271 free(bufferWithOutputPort);
272 bufferWithOutputPort = NULL;
278 void OMX_EGL_Render::printPortSettings(bool which)
280 OMX_PARAM_PORTDEFINITIONTYPE portSettings;
282 memset(&portSettings, 0, sizeof(portSettings));
283 portSettings.nSize = sizeof(portSettings);
284 portSettings.nVersion.nVersion = OMX_VERSION;
288 log->debug(TAG, "------ Port settings for OUTPUT:");
289 portSettings.nPortIndex = outputPort;
293 log->debug(TAG, "------ Port settings for INPUT:");
294 portSettings.nPortIndex = inputPort;
297 OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &portSettings);
298 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_EGL_Render::printPortSettings()", error);
300 log->debug(TAG, " nPortIndex: {}", portSettings.nPortIndex);
301 log->debug(TAG, " buffer count min, actual, size: {} {} {}", portSettings.nBufferCountMin, portSettings.nBufferCountActual, portSettings.nBufferSize);
302 log->debug(TAG, " eDir: {}", portSettings.eDir);
303 log->debug(TAG, " bEnabled: {}", portSettings.bEnabled);
304 log->debug(TAG, " bPopulated: {}", portSettings.bPopulated);
305 log->debug(TAG, " eDomain: {}", portSettings.eDomain);
307 log->debug(TAG, " cMIMEType: {}", static_cast<void*>(portSettings.format.video.cMIMEType));
308 log->debug(TAG, " pNativeRender: {}", static_cast<void*>(portSettings.format.video.pNativeRender));
309 log->debug(TAG, " nFrameWidth: {}", portSettings.format.video.nFrameWidth);
310 log->debug(TAG, " nFrameHeight: {}", portSettings.format.video.nFrameHeight);
311 log->debug(TAG, " nStride: {}", portSettings.format.video.nStride);
312 log->debug(TAG, " nSliceHeight: {}", portSettings.format.video.nSliceHeight);
313 log->debug(TAG, " nBitrate: {}", portSettings.format.video.nBitrate);
314 log->debug(TAG, " xFramerate: {}", portSettings.format.video.xFramerate);
315 log->debug(TAG, " eCompressionFormat: {:#x}", portSettings.format.video.eCompressionFormat);
316 log->debug(TAG, " eColorFormat: {:#x}", portSettings.format.video.eColorFormat);
319 OMX_ERRORTYPE OMX_EGL_Render::cb_EmptyBufferDone(OMX_IN OMX_HANDLETYPE /*hcomp*/, OMX_IN OMX_PTR /*appdata*/, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
322 log->debug(TAG, "CB: EmptyBufferDone");
323 log->debug(TAG, " nSize = {}", buffer->nSize);
324 log->debug(TAG, " nAllocLen = {}", buffer->nAllocLen);
325 log->debug(TAG, " nFilledLen = {}", buffer->nFilledLen);
326 log->debug(TAG, " nOffset = {}", buffer->nOffset);
327 log->debug(TAG, " nInputPortIndex = {}", buffer->nInputPortIndex);
328 log->debug(TAG, " nOutputPortIndex = {}", buffer->nOutputPortIndex);
329 log->debug(TAG, " nFlags = {:#x}", buffer->nFlags);
331 return OMX_ErrorNone;
334 OMX_ERRORTYPE OMX_EGL_Render::cb_FillBufferDone(OMX_IN OMX_HANDLETYPE hcomp, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
336 // log->debug(TAG, "CB: FillBufferDone, handle: {}, appdata: {}, buffer: {}", static_cast<void*>(hcomp), static_cast<void*>(appdata), static_cast<void*>(buffer));
338 struct BufferWithOutputPort* bufferWithOutputPort = static_cast<struct BufferWithOutputPort*>(buffer->pAppPrivate);
339 bufferWithOutputPort->mutex.lock();
340 bufferWithOutputPort->done = true;
341 bufferWithOutputPort->mutex.unlock();
342 bufferWithOutputPort->cond.notify_one();
344 return OMX_ErrorNone;