]> git.vomp.tv Git - vompclient.git/blob - omx/omximagedecode.cc
Rewrite of ImageOMX to fix the PNG problem
[vompclient.git] / omx / omximagedecode.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 "../log.h"
21
22 #include "omximagedecode.h"
23
24 static const char* TAG = "OMX_Image_Decode";
25
26 OMX_Image_Decode::OMX_Image_Decode()
27 {
28   omx_image_decode = this;
29 }
30
31 OMX_Image_Decode::~OMX_Image_Decode()
32 {
33   omx_image_decode = NULL;
34   handle_image_decode = 0;
35 }
36
37 bool OMX_Image_Decode::init()
38 {
39   initEventsProcessing();
40
41   OMX_CALLBACKTYPE callbacks = {&scb_EventHandler, &scb_EmptyBufferDone, &scb_FillBufferDone};
42
43   char* componentName;
44   asprintf(&componentName, "%s", componentName_image_decode);
45   OMX_ERRORTYPE error = OMX_GetHandle(&componentHandle, componentName, NULL, &callbacks);
46   free(componentName);
47   log->debug(TAG, "HANDLE ALLOC: omx_image_decode: {}", static_cast<void*>(componentHandle));
48   if (error != OMX_ErrorNone) return false;
49
50   handle_image_decode = componentHandle;
51
52   OMX_PORT_PARAM_TYPE p_param;
53   memset(&p_param, 0, sizeof(p_param));
54   p_param.nSize = sizeof(p_param);
55   p_param.nVersion.nVersion = OMX_VERSION;
56
57   error = OMX_GetParameter(componentHandle, OMX_IndexParamImageInit, &p_param);
58   if (error != OMX_ErrorNone) return false;
59
60   inputPort = p_param.nStartPortNumber;
61   outputPort = p_param.nStartPortNumber + 1;
62
63   log->debug(TAG, "IN: {}, OUT: {}", inputPort, outputPort);
64   return true;
65 }
66
67 void OMX_Image_Decode::shutdown()
68 {
69   stopEventsProcessing();
70   OMX_ERRORTYPE error = OMX_FreeHandle(componentHandle);
71   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeHandle in OMX_Image_Decode::shutdown()", error);
72 }
73
74 void OMX_Image_Decode::prepareInputBuffers(int tdataSize)
75 {
76   dataSize = tdataSize;
77
78   OMX_PARAM_PORTDEFINITIONTYPE port_def_type;
79   memset(&port_def_type, 0, sizeof(port_def_type));
80   port_def_type.nSize = sizeof(port_def_type);
81   port_def_type.nVersion.nVersion = OMX_VERSION;
82   port_def_type.nPortIndex = inputPort;
83
84   OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type);
85   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_Image_Decode::prepareInputBuffers()", error);
86
87   log->debug(TAG, "Defaults: bufferCountActual: {}, bufferCountMin: {}, nBufferSize: {}",
88         port_def_type.nBufferCountActual, port_def_type.nBufferCountMin, port_def_type.nBufferSize);
89
90   port_def_type.nBufferCountActual = port_def_type.nBufferCountMin;
91   if (dataSize > port_def_type.nBufferSize) port_def_type.nBufferSize = dataSize;
92
93   log->debug(TAG, "Setting buf count actual: {}, buf size: {}",
94         port_def_type.nBufferCountActual, port_def_type.nBufferSize);
95
96   error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type);
97   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_Image_Decode::prepareInputBuffers()", error);
98 }
99
100 void OMX_Image_Decode::setFormat()
101 {
102   OMX_IMAGE_PARAM_PORTFORMATTYPE ft_type;
103   memset(&ft_type, 0, sizeof(ft_type));
104   ft_type.nSize = sizeof(ft_type);
105   ft_type.nVersion.nVersion = OMX_VERSION;
106   ft_type.eCompressionFormat = OMX_IMAGE_CodingPNG;
107   ft_type.nPortIndex = inputPort;
108
109   // Would use this function to select between PNG and JPEG but image_decode
110   // only outputs JPEG decoded images in YUV420, because of course it does.
111   // ft_type.eCompressionFormat = OMX_IMAGE_CodingJPEG;
112
113   OMX_ERRORTYPE error = OMX_SetParameter(componentHandle, OMX_IndexParamImagePortFormat, &ft_type);
114   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_Image_Decode::setFormat()", error);
115   log->debug(TAG, "setFormat OK");
116 }
117
118 void OMX_Image_Decode::setSliceHeight(int newSliceHeight)
119 {
120   outputPortSettings.format.image.nSliceHeight = newSliceHeight;
121   log->debug(TAG, "nSliceHeight: {}", outputPortSettings.format.image.nSliceHeight);
122
123   OMX_ERRORTYPE error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &outputPortSettings);
124   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_Image_Decode::setSliceHeight()", error);
125
126   {
127     // Dump new independant output settings
128     OMX_PARAM_PORTDEFINITIONTYPE indTest;
129     memset(&indTest, 0, sizeof(indTest));
130     indTest.nSize = sizeof(indTest);
131     indTest.nVersion.nVersion = OMX_VERSION;
132     indTest.nPortIndex = outputPort;
133
134     error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &indTest);
135     if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_Image_Decode::setSliceHeight()", error);
136
137     log->debug(TAG, "Port def type from outputPort");
138     log->debug(TAG, "buffer count actual: {}", indTest.nBufferCountActual);
139     log->debug(TAG, "eDir: {}", indTest.eDir);
140     log->debug(TAG, "nBufferSize: {}", indTest.nBufferSize);
141     log->debug(TAG, "bEnabled: {}", indTest.bEnabled);
142     log->debug(TAG, "bPopulated: {}", indTest.bPopulated);
143     log->debug(TAG, "eDomain: {}", indTest.eDomain);
144
145     log->debug(TAG, "cMIMEType: {}", static_cast<void*>(indTest.format.image.cMIMEType));
146     log->debug(TAG, "pNativeRender: {}", static_cast<void*>(indTest.format.image.pNativeRender));
147     log->debug(TAG, "nFrameWidth: {}", indTest.format.image.nFrameWidth);
148     log->debug(TAG, "nFrameHeight: {}", indTest.format.image.nFrameHeight);
149     log->debug(TAG, "nStride: {}", indTest.format.image.nStride);
150     log->debug(TAG, "nSliceHeight: {}", indTest.format.image.nSliceHeight);
151     log->debug(TAG, "eColorFormat: {:#x}", indTest.format.image.eColorFormat);
152   }
153 }
154
155 void OMX_Image_Decode::allocateInputBuffers(char* data)
156 {
157   inBuffer1 = NULL;
158   inBuffer2 = NULL;
159
160   log->debug(TAG, "Calling OMX_UseBuffer");
161   OMX_ERRORTYPE error = OMX_UseBuffer(componentHandle, &inBuffer1, inputPort, static_cast<OMX_PTR>(0), dataSize, reinterpret_cast<OMX_U8*>(data));
162   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer 1 in OMX_Image_Decode::allocateInputBuffers()", error);
163
164   // Hardcoded assumption - image_decode has min 2 input buffers
165   error = OMX_UseBuffer(componentHandle, &inBuffer2, inputPort, static_cast<OMX_PTR>(0), 0, NULL);
166   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer 2 in OMX_Image_Decode::allocateInputBuffers()", error);
167 }
168
169 void OMX_Image_Decode::allocateOutputBuffer()
170 {
171   OMX_ERRORTYPE error = OMX_UseBuffer(componentHandle, &outputBufferHeader, outputPort, static_cast<OMX_PTR>(0), outputPortSettings.nBufferSize, (OMX_U8*)NULL);
172   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer in OMX_Image_Decode::allocateOutputBuffer()", error);
173 }
174
175 void OMX_Image_Decode::deallocateInputBuffers()
176 {
177   OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, inputPort, inBuffer1);
178   inBuffer1 = NULL;
179   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer 1 in OMX_Image_Decode::deallocateInputBuffers()", error);
180
181   error = OMX_FreeBuffer(componentHandle, inputPort, inBuffer2);
182   inBuffer2 = NULL;
183   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer 2 in OMX_Image_Decode::deallocateInputBuffers()", error);
184 }
185
186 void OMX_Image_Decode::deallocateOutputBuffer()
187 {
188   OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, outputPort, outputBufferHeader);
189   outputBufferHeader = NULL;
190   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer in OMX_Image_Decode::deallocateOutputBuffer()", error);
191 }
192
193 void OMX_Image_Decode::sendToInput()
194 {
195   log->debug(TAG, "Starting sendToInput");
196   inBuffer1->nFilledLen = dataSize;
197   inBuffer1->nOffset = 0;
198   inBuffer1->nTimeStamp = { 0, 0 };
199   inBuffer1->pAppPrivate = static_cast<OMX_PTR>(0);
200   inBuffer1->nFlags |= OMX_BUFFERFLAG_EOS;
201
202   log->debug(TAG, "calling emptythisbuffer");
203   OMX_ERRORTYPE error = OMX_EmptyThisBuffer(componentHandle, inBuffer1);
204   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_EmptyThisBuffer in OMX_Image_Decode::sendToInput()", error);
205   log->debug(TAG, "sendToInput done");
206 }
207
208 void OMX_Image_Decode::waitForOutputPortSettingsChange()
209 {
210   eventsProcessorMutex.lock();
211   std::unique_lock<std::mutex> ul(eventWaitersMutex);
212
213   EventWaiter* eventWaiter = new EventWaiter();
214   eventWaiter->waiting = true;
215   eventWaiter->eventType = OMX_EventPortSettingsChanged;
216   eventWaiter->port = outputPort;
217   eventWaiters.push_back(eventWaiter);
218
219   eventsProcessorWake = true;
220   eventsProcessorMutex.unlock();
221   eventsProcessorCond.notify_one();
222
223   eventWaiter->cond.wait(ul);  // sleep this thread
224   ul.unlock();
225
226   if (!eventWaiter->receivedEvent)
227   {
228     delete eventWaiter;
229     throw OMX_Exception("Error waiting for event in waitForOutputPortSettingsChange", 0);
230   }
231
232   // The event processor thread received an event and saved it here
233   log->debug(TAG, "received outputportsettingschanged event!");
234   delete eventWaiter->receivedEvent;
235   delete eventWaiter;
236
237   memset(&outputPortSettings, 0, sizeof(outputPortSettings));
238   outputPortSettings.nSize = sizeof(outputPortSettings);
239   outputPortSettings.nVersion.nVersion = OMX_VERSION;
240   outputPortSettings.nPortIndex = outputPort;
241
242   OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &outputPortSettings);
243   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_Image_Decode::waitForOutputPortSettingsChange()", error);
244
245   log->debug(TAG, "Port def type from outputPort");
246   log->debug(TAG, "buffer count actual: {}", outputPortSettings.nBufferCountActual);
247   log->debug(TAG, "eDir: {}", outputPortSettings.eDir);
248   log->debug(TAG, "nBufferSize: {}", outputPortSettings.nBufferSize);
249   log->debug(TAG, "bEnabled: {}", outputPortSettings.bEnabled);
250   log->debug(TAG, "bPopulated: {}", outputPortSettings.bPopulated);
251   log->debug(TAG, "eDomain: {}", outputPortSettings.eDomain);
252
253   log->debug(TAG, "cMIMEType: {}", static_cast<void*>(outputPortSettings.format.image.cMIMEType));
254   log->debug(TAG, "pNativeRender: {}", static_cast<void*>(outputPortSettings.format.image.pNativeRender));
255   log->debug(TAG, "nFrameWidth: {}", outputPortSettings.format.image.nFrameWidth);
256   log->debug(TAG, "nFrameHeight: {}", outputPortSettings.format.image.nFrameHeight);
257   log->debug(TAG, "nStride: {}", outputPortSettings.format.image.nStride);
258   log->debug(TAG, "nSliceHeight: {}", outputPortSettings.format.image.nSliceHeight);
259 }
260
261 void OMX_Image_Decode::receiveFromOutput(char** data, int* nFlags)
262 {
263   void* outputBufferMem{};
264
265   try
266   {
267     outputBufferMem = malloc(outputPortSettings.nBufferSize);
268     memset(outputBufferMem, 0, outputPortSettings.nBufferSize);
269     outputBufferHeader->pBuffer = static_cast<OMX_U8*>(outputBufferMem);
270
271     struct BufferWithOutputPort* bufferWithOutputPort = new BufferWithOutputPort();
272     bufferWithOutputPort->bufhead = outputBufferHeader;
273     outputBufferHeader->pAppPrivate = static_cast<OMX_PTR>(bufferWithOutputPort);
274
275     std::unique_lock<std::mutex> ul(bufferWithOutputPort->mutex);
276
277     log->debug(TAG, "calling fillthisbuffer. bufhead: {}, BufferWithOutput: {}", static_cast<void*>(outputBufferHeader), static_cast<void*>(bufferWithOutputPort));
278
279     OMX_ERRORTYPE error;
280
281     error = OMX_FillThisBuffer(componentHandle, outputBufferHeader);
282     if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FillThisBuffer in OMX_Image_Decode::receiveFromOutput()", error);
283
284     log->debug(TAG, "called fillthisbuffer. bufsize = {}", outputPortSettings.nBufferSize);
285
286     bufferWithOutputPort->cond.wait(ul, [bufferWithOutputPort] { return bufferWithOutputPort->done; });
287     ul.unlock();
288
289     log->debug(TAG, "receiveFromOutputPort signalled");
290
291     *data = static_cast<char*>(outputBufferMem); // caller takes ownership
292     *nFlags = outputBufferHeader->nFlags;
293
294     outputBufferMem = NULL;
295
296     // The slice height is all important - can't trust nFilledLen
297
298     log->debug(TAG, "receiveFromOutput:");
299     log->debug(TAG, "  nSize = {}", outputBufferHeader->nSize);
300     log->debug(TAG, "  nAllocLen = {}", outputBufferHeader->nAllocLen);
301     log->debug(TAG, "  nFilledLen = {}", outputBufferHeader->nFilledLen);
302     log->debug(TAG, "  nOffset = {}", outputBufferHeader->nOffset);
303     log->debug(TAG, "  nInputPortIndex = {}", outputBufferHeader->nInputPortIndex);
304     log->debug(TAG, "  nOutputPortIndex = {}", outputBufferHeader->nOutputPortIndex);
305     log->debug(TAG, "  nFlags = {:#x}", outputBufferHeader->nFlags);
306
307     delete bufferWithOutputPort;
308   }
309   catch(...)
310   {
311     if (outputBufferMem)
312     {
313       free(outputBufferMem);
314       outputBufferMem = NULL;
315     }
316     throw;
317   }
318 }
319
320 void OMX_Image_Decode::getImageInfo(int* width, int* height, int* stride, int* sliceHeight)
321 {
322   *width = outputPortSettings.format.image.nFrameWidth;
323   *height = outputPortSettings.format.image.nFrameHeight;
324   *stride = outputPortSettings.format.image.nStride;
325   *sliceHeight = outputPortSettings.format.image.nSliceHeight;
326 }
327
328 OMX_ERRORTYPE OMX_Image_Decode::cb_EmptyBufferDone(OMX_IN OMX_HANDLETYPE /*hcomp*/, OMX_IN OMX_PTR /*appdata*/, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
329 {
330   log->debug(TAG, "CB: EmptyBufferDone");
331   log->debug(TAG, "  nSize = {}", buffer->nSize);
332   log->debug(TAG, "  nAllocLen = {}", buffer->nAllocLen);
333   log->debug(TAG, "  nFilledLen = {}", buffer->nFilledLen);
334   log->debug(TAG, "  nOffset = {}", buffer->nOffset);
335   log->debug(TAG, "  nInputPortIndex = {}", buffer->nInputPortIndex);
336   log->debug(TAG, "  nOutputPortIndex = {}", buffer->nOutputPortIndex);
337   log->debug(TAG, "  nFlags = {:#x}", buffer->nFlags);
338
339   return OMX_ErrorNone;
340 }
341
342 OMX_ERRORTYPE OMX_Image_Decode::cb_FillBufferDone(OMX_IN OMX_HANDLETYPE hcomp, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
343 {
344   log->debug(TAG, "CB: FillBufferDone, handle: {}, appdata: {}, buffer: {}", static_cast<void*>(hcomp), static_cast<void*>(appdata), static_cast<void*>(buffer));
345
346   struct BufferWithOutputPort* bufferWithOutputPort = static_cast<struct BufferWithOutputPort*>(buffer->pAppPrivate);
347   bufferWithOutputPort->mutex.lock();
348   bufferWithOutputPort->done = true;
349   bufferWithOutputPort->mutex.unlock();
350   bufferWithOutputPort->cond.notify_one();
351
352   return OMX_ErrorNone;
353 }