]> git.vomp.tv Git - vompclient.git/blob - omx/omxeglrender.cc
Rewrite of ImageOMX to fix the PNG problem
[vompclient.git] / omx / omxeglrender.cc
1 /*
2     Copyright 2021 Chris Tallon
3
4     This file is part of VOMP.
5
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.
10
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.
15
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/>.
18 */
19
20 #include <VG/openvg.h>
21 #include <EGL/egl.h>
22 #include <EGL/eglext.h>
23
24 #include "../log.h"
25
26 #include "omxeglrender.h"
27
28 static const char* TAG = "OMX_EGL_Render";
29
30
31 OMX_EGL_Render::OMX_EGL_Render()
32 {
33   omx_egl_render = this;
34 }
35
36 OMX_EGL_Render::~OMX_EGL_Render()
37 {
38   omx_image_decode = NULL;
39   handle_egl_render = 0;
40 }
41
42 bool OMX_EGL_Render::init()
43 {
44   initEventsProcessing();
45
46   OMX_CALLBACKTYPE callbacks = {&scb_EventHandler, &scb_EmptyBufferDone, &scb_FillBufferDone};
47
48   char* componentName;
49   asprintf(&componentName, "%s", componentName_egl_render);
50   OMX_ERRORTYPE error = OMX_GetHandle(&componentHandle, componentName, NULL, &callbacks);
51   free(componentName);
52   log->debug(TAG, "HANDLE ALLOC: OMX_EGL_Render: {} error: {:#x}", static_cast<void*>(componentHandle), error);
53   if (error != OMX_ErrorNone) return false;
54
55   handle_egl_render = componentHandle;
56
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;
61
62   error = OMX_GetParameter(componentHandle, OMX_IndexParamVideoInit, &p_param);
63   if (error != OMX_ErrorNone) return false;
64
65   inputPort = p_param.nStartPortNumber;
66   outputPort = p_param.nStartPortNumber + 1;
67
68   log->debug(TAG, "IN: {}, OUT: {}", inputPort, outputPort);
69   return true;
70 }
71
72 void OMX_EGL_Render::shutdown()
73 {
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);
77 }
78
79 void OMX_EGL_Render::prepareInputPort(OMX_U32 frameWidth, OMX_U32 frameHeight, OMX_U32 stride)
80 {
81   log->debug(TAG, "prepareInputPort");
82
83   // Calculate a slice height. It must be divisible by 16 and >= frameHeight
84   // Temporarily use sliceHeight for remainder
85   int sliceHeight = frameHeight % 16;
86   if (sliceHeight == 0)
87     sliceHeight = frameHeight;
88   else
89     sliceHeight = frameHeight + 16 - sliceHeight;
90
91   // Buffer size IS THE SLICE HEIGHT multiplied by the stride
92   inputPortBufferSize = sliceHeight * stride;
93
94
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;
100
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);
103
104   log->debug(TAG, "Recv from get:");
105   log->debug(TAG, "nPortIndex: {}", portConfig.nPortIndex);
106   log->debug(TAG, "eDir: {}", portConfig.eDir);
107   log->debug(TAG, "buffer count actual: {}", portConfig.nBufferCountActual);
108   log->debug(TAG, "buffer count min: {}", portConfig.nBufferCountMin);
109   log->debug(TAG, "nBufferSize: {}", portConfig.nBufferSize);
110   log->debug(TAG, "bEnabled: {}", portConfig.bEnabled);
111   log->debug(TAG, "bPopulated: {}", portConfig.bPopulated);
112   log->debug(TAG, "eDomain: {}", portConfig.eDomain);
113
114   portConfig.nBufferCountActual = 1;
115   portConfig.nBufferSize = inputPortBufferSize;
116   portConfig.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
117   portConfig.format.video.eColorFormat = OMX_COLOR_Format32bitABGR8888;
118   portConfig.format.video.nFrameWidth = frameWidth;
119   portConfig.format.video.nStride = stride;
120   portConfig.format.video.nFrameHeight = frameHeight;
121   portConfig.format.video.nSliceHeight = sliceHeight;
122
123   log->debug(TAG, "Setting buf count actual: {}, buf size: {}", portConfig.nBufferCountActual, portConfig.nBufferSize);
124   log->debug(TAG, "calling setParameter: w {} h {} str {} sli {}", portConfig.format.video.nFrameWidth, portConfig.format.video.nFrameHeight,
125     portConfig.format.video.nStride, portConfig.format.video.nSliceHeight);
126
127   error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &portConfig);
128   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_EGL_Render::prepareInputPort()", error);
129
130   printPortSettings(false);
131 }
132
133 void OMX_EGL_Render::prepareOutputPort(EGLDisplay egldisplay)
134 {
135   OMX_PARAM_PORTDEFINITIONTYPE port_def_type;
136   memset(&port_def_type, 0, sizeof(port_def_type));
137   port_def_type.nSize = sizeof(port_def_type);
138   port_def_type.nVersion.nVersion = OMX_VERSION;
139   port_def_type.nPortIndex = outputPort;
140
141   OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type);
142   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_EGL_Render::prep()", error);
143
144   port_def_type.nBufferCountActual = 1;
145   port_def_type.format.video.pNativeWindow = egldisplay;
146
147   error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type);  // set port def: buffer count actual = 1
148   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_EGL_Render::prep()", error);
149 }
150
151 void OMX_EGL_Render::allocateInputBuffers(char* data)
152 {
153   inBuffer1 = NULL;
154
155   log->debug(TAG, "Calling OMX_UseBuffer with data = {}", static_cast<void*>(data));
156   OMX_ERRORTYPE error = OMX_UseBuffer(componentHandle, &inBuffer1, inputPort, static_cast<OMX_PTR>(0), inputPortBufferSize, reinterpret_cast<OMX_U8*>(data));
157   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer in OMX_EGL_Render::allocateInputBuffers()", error);
158
159   log->debug(TAG, "OMX_UseBuffer:");
160   log->debug(TAG, "  nSize = {}", inBuffer1->nSize);
161   log->debug(TAG, "  pBuffer = {}", static_cast<void*>(inBuffer1->pBuffer));
162   log->debug(TAG, "  nAllocLen = {}", inBuffer1->nAllocLen);
163   log->debug(TAG, "  nFilledLen = {}", inBuffer1->nFilledLen);
164   log->debug(TAG, "  nOffset = {}", inBuffer1->nOffset);
165   log->debug(TAG, "  nInputPortIndex = {}", inBuffer1->nInputPortIndex);
166   log->debug(TAG, "  nOutputPortIndex = {}", inBuffer1->nOutputPortIndex);
167   log->debug(TAG, "  nFlags = {:#x}", inBuffer1->nFlags);
168
169   printPortSettings(false);
170 }
171
172 void OMX_EGL_Render::allocateEGLImageKHR(EGLImageKHR eglimagekhr)
173 {
174   OMX_ERRORTYPE error = OMX_UseEGLImage(componentHandle, &eglRenderOutputBufferHeader, outputPort, this, static_cast<void*>(eglimagekhr));
175   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseEGLImage in OMX_EGL_Render::allocateEGLImageKHR()", error);
176
177   printPortSettings(true);
178 }
179
180 void OMX_EGL_Render::deallocateInputBuffers()
181 {
182   OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, inputPort, inBuffer1);
183   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer in OMX_EGL_Render::deallocateInputBuffers()", error);
184
185   inBuffer1 = NULL;
186 }
187
188 void OMX_EGL_Render::deallocateEGLImageKHR()
189 {
190   OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, outputPort, eglRenderOutputBufferHeader);
191   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer in OMX_EGL_Render::deallocateEGLImageKHR()", error);
192
193   eglRenderOutputBufferHeader = NULL;
194 }
195
196 void OMX_EGL_Render::sendToInput(char* data, int dataSize)
197 {
198   log->debug(TAG, "Starting sendToInput {}", dataSize);
199
200   inBuffer1->nFilledLen = dataSize;
201   inBuffer1->nOffset = 0;
202   inBuffer1->nTimeStamp = { 0, 0 };
203   inBuffer1->pAppPrivate = static_cast<OMX_PTR>(0);
204   inBuffer1->nFlags |= OMX_BUFFERFLAG_EOS;
205   inBuffer1->pBuffer = reinterpret_cast<OMX_U8*>(data);
206
207   log->debug(TAG, "calling emptythisbuffer");
208   OMX_ERRORTYPE error = OMX_EmptyThisBuffer(componentHandle, inBuffer1);
209   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_EmptyThisBuffer in OMX_EGL_Render::sendToInput()", error);
210   log->debug(TAG, "sendToInput done");
211 }
212
213 void OMX_EGL_Render::render()
214 {
215   struct BufferWithOutputPort* bufferWithOutputPort{};
216
217   try
218   {
219     bufferWithOutputPort = new BufferWithOutputPort();
220     bufferWithOutputPort->bufhead = eglRenderOutputBufferHeader;
221     eglRenderOutputBufferHeader->pAppPrivate = static_cast<OMX_PTR>(bufferWithOutputPort);
222
223     log->debug(TAG, "render: egl buffer header:");
224     log->debug(TAG, "{}", static_cast<void*>(eglRenderOutputBufferHeader->pBuffer));
225     log->debug(TAG, "{}", eglRenderOutputBufferHeader->nAllocLen);
226     log->debug(TAG, "{}", eglRenderOutputBufferHeader->nFilledLen);
227     log->debug(TAG, "{}", eglRenderOutputBufferHeader->nOffset);
228     log->debug(TAG, "{:#x}", eglRenderOutputBufferHeader->nFlags);
229     log->debug(TAG, "{}", eglRenderOutputBufferHeader->nInputPortIndex);
230     log->debug(TAG, "{}", eglRenderOutputBufferHeader->nOutputPortIndex);
231     log->debug(TAG, "{}", static_cast<void*>(eglRenderOutputBufferHeader->pAppPrivate));
232
233
234     std::unique_lock<std::mutex> ul(bufferWithOutputPort->mutex);
235
236     log->debug(TAG, "calling fillthisbuffer. bufhead: {}, BufferWithOutput: {}", static_cast<void*>(eglRenderOutputBufferHeader), static_cast<void*>(bufferWithOutputPort));
237
238     OMX_ERRORTYPE error = OMX_FillThisBuffer(componentHandle, eglRenderOutputBufferHeader);
239     if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FillThisBuffer in OMX_EGL_Render::render()", error);
240
241     log->debug(TAG, "done fillthisbuffer. bufhead: {}, BufferWithOutput: {}", static_cast<void*>(eglRenderOutputBufferHeader), static_cast<void*>(bufferWithOutputPort));
242
243     bufferWithOutputPort->cond.wait(ul, [bufferWithOutputPort] { return bufferWithOutputPort->done; });
244     ul.unlock();
245
246     log->debug(TAG, "receiveFromOutputPort signalled");
247
248     log->debug(TAG, "receiveFromOutput:");
249     log->debug(TAG, "  nSize = {}", eglRenderOutputBufferHeader->nSize);
250     log->debug(TAG, "  nAllocLen = {}", eglRenderOutputBufferHeader->nAllocLen);
251     log->debug(TAG, "  nFilledLen = {}", eglRenderOutputBufferHeader->nFilledLen);
252     log->debug(TAG, "  nOffset = {}", eglRenderOutputBufferHeader->nOffset);
253     log->debug(TAG, "  nInputPortIndex = {}", eglRenderOutputBufferHeader->nInputPortIndex);
254     log->debug(TAG, "  nOutputPortIndex = {}", eglRenderOutputBufferHeader->nOutputPortIndex);
255     log->debug(TAG, "  nFlags = {:#x}", eglRenderOutputBufferHeader->nFlags);
256
257     delete bufferWithOutputPort;
258     bufferWithOutputPort = NULL;
259   }
260   catch(...)
261   {
262     if (bufferWithOutputPort)
263     {
264       free(bufferWithOutputPort);
265       bufferWithOutputPort = NULL;
266     }
267     throw;
268   }
269 }
270
271 void OMX_EGL_Render::printPortSettings(bool which)
272 {
273   OMX_PARAM_PORTDEFINITIONTYPE portSettings;
274
275   memset(&portSettings, 0, sizeof(portSettings));
276   portSettings.nSize = sizeof(portSettings);
277   portSettings.nVersion.nVersion = OMX_VERSION;
278
279   if (which)
280   {
281     log->debug(TAG, "------ Port settings for OUTPUT:");
282     portSettings.nPortIndex = outputPort;
283   }
284   else
285   {
286     log->debug(TAG, "------ Port settings for INPUT:");
287     portSettings.nPortIndex = inputPort;
288   }
289
290   OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &portSettings);
291   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_EGL_Render::printPortSettings()", error);
292
293   log->debug(TAG, "  nPortIndex: {}", portSettings.nPortIndex);
294   log->debug(TAG, "  buffer count min, actual, size: {} {} {}", portSettings.nBufferCountMin, portSettings.nBufferCountActual, portSettings.nBufferSize);
295   log->debug(TAG, "  eDir: {}", portSettings.eDir);
296   log->debug(TAG, "  bEnabled: {}", portSettings.bEnabled);
297   log->debug(TAG, "  bPopulated: {}", portSettings.bPopulated);
298   log->debug(TAG, "  eDomain: {}", portSettings.eDomain);
299
300   log->debug(TAG, "  cMIMEType: {}", static_cast<void*>(portSettings.format.video.cMIMEType));
301   log->debug(TAG, "  pNativeRender: {}", static_cast<void*>(portSettings.format.video.pNativeRender));
302   log->debug(TAG, "  nFrameWidth: {}", portSettings.format.video.nFrameWidth);
303   log->debug(TAG, "  nFrameHeight: {}", portSettings.format.video.nFrameHeight);
304   log->debug(TAG, "  nStride: {}", portSettings.format.video.nStride);
305   log->debug(TAG, "  nSliceHeight: {}", portSettings.format.video.nSliceHeight);
306   log->debug(TAG, "  nBitrate: {}", portSettings.format.video.nBitrate);
307   log->debug(TAG, "  xFramerate: {}", portSettings.format.video.xFramerate);
308   log->debug(TAG, "  eCompressionFormat: {:#x}", portSettings.format.video.eCompressionFormat);
309   log->debug(TAG, "  eColorFormat: {:#x}", portSettings.format.video.eColorFormat);
310 }
311
312 OMX_ERRORTYPE OMX_EGL_Render::cb_EmptyBufferDone(OMX_IN OMX_HANDLETYPE /*hcomp*/, OMX_IN OMX_PTR /*appdata*/, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
313 {
314   log->debug(TAG, "CB: EmptyBufferDone");
315   log->debug(TAG, "  nSize = {}", buffer->nSize);
316   log->debug(TAG, "  nAllocLen = {}", buffer->nAllocLen);
317   log->debug(TAG, "  nFilledLen = {}", buffer->nFilledLen);
318   log->debug(TAG, "  nOffset = {}", buffer->nOffset);
319   log->debug(TAG, "  nInputPortIndex = {}", buffer->nInputPortIndex);
320   log->debug(TAG, "  nOutputPortIndex = {}", buffer->nOutputPortIndex);
321   log->debug(TAG, "  nFlags = {:#x}", buffer->nFlags);
322   return OMX_ErrorNone;
323 }
324
325 OMX_ERRORTYPE OMX_EGL_Render::cb_FillBufferDone(OMX_IN OMX_HANDLETYPE hcomp, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
326 {
327   log->debug(TAG, "CB: FillBufferDone, handle: {}, appdata: {}, buffer: {}", static_cast<void*>(hcomp), static_cast<void*>(appdata), static_cast<void*>(buffer));
328
329   struct BufferWithOutputPort* bufferWithOutputPort = static_cast<struct BufferWithOutputPort*>(buffer->pAppPrivate);
330   bufferWithOutputPort->mutex.lock();
331   bufferWithOutputPort->done = true;
332   bufferWithOutputPort->mutex.unlock();
333   bufferWithOutputPort->cond.notify_one();
334
335   return OMX_ErrorNone;
336 }