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/>.
22 #include "omximagedecode.h"
24 // static const char* TAG = "OMX_Image_Decode";
26 OMX_Image_Decode::OMX_Image_Decode()
28 omx_image_decode = this;
31 OMX_Image_Decode::~OMX_Image_Decode()
33 omx_image_decode = NULL;
34 handle_image_decode = 0;
37 bool OMX_Image_Decode::init()
39 initEventsProcessing();
41 OMX_CALLBACKTYPE callbacks = {&scb_EventHandler, &scb_EmptyBufferDone, &scb_FillBufferDone};
44 asprintf(&componentName, "%s", componentName_image_decode);
45 OMX_ERRORTYPE error = OMX_GetHandle(&componentHandle, componentName, NULL, &callbacks);
47 // log->debug(TAG, "HANDLE ALLOC: omx_image_decode: {}", static_cast<void*>(componentHandle));
48 if (error != OMX_ErrorNone) return false;
50 handle_image_decode = componentHandle;
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;
57 #pragma GCC diagnostic push
58 #pragma GCC diagnostic ignored "-Wold-style-cast"
59 error = OMX_GetParameter(componentHandle, OMX_IndexParamImageInit, &p_param);
60 #pragma GCC diagnostic pop
61 if (error != OMX_ErrorNone) return false;
63 inputPort = p_param.nStartPortNumber;
64 outputPort = p_param.nStartPortNumber + 1;
66 // log->debug(TAG, "IN: {}, OUT: {}", inputPort, outputPort);
70 void OMX_Image_Decode::shutdown()
72 stopEventsProcessing();
73 OMX_ERRORTYPE error = OMX_FreeHandle(componentHandle);
74 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeHandle in OMX_Image_Decode::shutdown()", error);
77 void OMX_Image_Decode::prepareInputBuffers(int tdataSize)
81 OMX_PARAM_PORTDEFINITIONTYPE port_def_type;
82 memset(&port_def_type, 0, sizeof(port_def_type));
83 port_def_type.nSize = sizeof(port_def_type);
84 port_def_type.nVersion.nVersion = OMX_VERSION;
85 port_def_type.nPortIndex = inputPort;
87 #pragma GCC diagnostic push
88 #pragma GCC diagnostic ignored "-Wold-style-cast"
89 OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type);
90 #pragma GCC diagnostic pop
91 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_Image_Decode::prepareInputBuffers()", error);
93 // log->debug(TAG, "Defaults: bufferCountActual: {}, bufferCountMin: {}, nBufferSize: {}",
94 // port_def_type.nBufferCountActual, port_def_type.nBufferCountMin, port_def_type.nBufferSize);
96 port_def_type.nBufferCountActual = port_def_type.nBufferCountMin;
97 if (dataSize > port_def_type.nBufferSize) port_def_type.nBufferSize = dataSize;
99 // log->debug(TAG, "Setting buf count actual: {}, buf size: {}",
100 // port_def_type.nBufferCountActual, port_def_type.nBufferSize);
102 #pragma GCC diagnostic push
103 #pragma GCC diagnostic ignored "-Wold-style-cast"
104 error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &port_def_type);
105 #pragma GCC diagnostic pop
106 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_Image_Decode::prepareInputBuffers()", error);
109 void OMX_Image_Decode::setFormat()
111 OMX_IMAGE_PARAM_PORTFORMATTYPE ft_type;
112 memset(&ft_type, 0, sizeof(ft_type));
113 ft_type.nSize = sizeof(ft_type);
114 ft_type.nVersion.nVersion = OMX_VERSION;
115 ft_type.eCompressionFormat = OMX_IMAGE_CodingPNG;
116 ft_type.nPortIndex = inputPort;
118 // Would use this function to select between PNG and JPEG but image_decode
119 // only outputs JPEG decoded images in YUV420, because of course it does.
120 // ft_type.eCompressionFormat = OMX_IMAGE_CodingJPEG;
122 #pragma GCC diagnostic push
123 #pragma GCC diagnostic ignored "-Wold-style-cast"
124 OMX_ERRORTYPE error = OMX_SetParameter(componentHandle, OMX_IndexParamImagePortFormat, &ft_type);
125 #pragma GCC diagnostic pop
126 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_Image_Decode::setFormat()", error);
127 // log->debug(TAG, "setFormat OK");
130 void OMX_Image_Decode::setSliceHeight(int newSliceHeight)
132 outputPortSettings.format.image.nSliceHeight = newSliceHeight;
133 // log->debug(TAG, "nSliceHeight: {}", outputPortSettings.format.image.nSliceHeight);
135 #pragma GCC diagnostic push
136 #pragma GCC diagnostic ignored "-Wold-style-cast"
137 OMX_ERRORTYPE error = OMX_SetParameter(componentHandle, OMX_IndexParamPortDefinition, &outputPortSettings);
138 #pragma GCC diagnostic pop
139 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SetParameter in OMX_Image_Decode::setSliceHeight()", error);
143 // Dump new independant output settings
144 OMX_PARAM_PORTDEFINITIONTYPE indTest;
145 memset(&indTest, 0, sizeof(indTest));
146 indTest.nSize = sizeof(indTest);
147 indTest.nVersion.nVersion = OMX_VERSION;
148 indTest.nPortIndex = outputPort;
150 error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &indTest);
151 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_Image_Decode::setSliceHeight()", error);
153 log->debug(TAG, "Port def type from outputPort");
154 log->debug(TAG, "buffer count actual: {}", indTest.nBufferCountActual);
155 log->debug(TAG, "eDir: {}", indTest.eDir);
156 log->debug(TAG, "nBufferSize: {}", indTest.nBufferSize);
157 log->debug(TAG, "bEnabled: {}", indTest.bEnabled);
158 log->debug(TAG, "bPopulated: {}", indTest.bPopulated);
159 log->debug(TAG, "eDomain: {}", indTest.eDomain);
161 log->debug(TAG, "cMIMEType: {}", static_cast<void*>(indTest.format.image.cMIMEType));
162 log->debug(TAG, "pNativeRender: {}", static_cast<void*>(indTest.format.image.pNativeRender));
163 log->debug(TAG, "nFrameWidth: {}", indTest.format.image.nFrameWidth);
164 log->debug(TAG, "nFrameHeight: {}", indTest.format.image.nFrameHeight);
165 log->debug(TAG, "nStride: {}", indTest.format.image.nStride);
166 log->debug(TAG, "nSliceHeight: {}", indTest.format.image.nSliceHeight);
167 log->debug(TAG, "eColorFormat: {:#x}", indTest.format.image.eColorFormat);
172 void OMX_Image_Decode::allocateInputBuffers(char* data)
177 // log->debug(TAG, "Calling OMX_UseBuffer");
178 #pragma GCC diagnostic push
179 #pragma GCC diagnostic ignored "-Wold-style-cast"
180 OMX_ERRORTYPE error = OMX_UseBuffer(componentHandle, &inBuffer1, inputPort, static_cast<OMX_PTR>(0), dataSize, reinterpret_cast<OMX_U8*>(data));
181 #pragma GCC diagnostic pop
182 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer 1 in OMX_Image_Decode::allocateInputBuffers()", error);
184 // Hardcoded assumption - image_decode has min 2 input buffers
185 #pragma GCC diagnostic push
186 #pragma GCC diagnostic ignored "-Wold-style-cast"
187 error = OMX_UseBuffer(componentHandle, &inBuffer2, inputPort, static_cast<OMX_PTR>(0), 0, NULL);
188 #pragma GCC diagnostic pop
189 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer 2 in OMX_Image_Decode::allocateInputBuffers()", error);
192 void OMX_Image_Decode::allocateOutputBuffer()
194 #pragma GCC diagnostic push
195 #pragma GCC diagnostic ignored "-Wold-style-cast"
196 OMX_ERRORTYPE error = OMX_UseBuffer(componentHandle, &outputBufferHeader, outputPort, static_cast<OMX_PTR>(0), outputPortSettings.nBufferSize, (OMX_U8*)NULL);
197 #pragma GCC diagnostic pop
198 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_UseBuffer in OMX_Image_Decode::allocateOutputBuffer()", error);
201 void OMX_Image_Decode::deallocateInputBuffers()
203 #pragma GCC diagnostic push
204 #pragma GCC diagnostic ignored "-Wold-style-cast"
205 OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, inputPort, inBuffer1);
206 #pragma GCC diagnostic pop
208 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer 1 in OMX_Image_Decode::deallocateInputBuffers()", error);
210 #pragma GCC diagnostic push
211 #pragma GCC diagnostic ignored "-Wold-style-cast"
212 error = OMX_FreeBuffer(componentHandle, inputPort, inBuffer2);
213 #pragma GCC diagnostic pop
215 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer 2 in OMX_Image_Decode::deallocateInputBuffers()", error);
218 void OMX_Image_Decode::deallocateOutputBuffer()
220 #pragma GCC diagnostic push
221 #pragma GCC diagnostic ignored "-Wold-style-cast"
222 OMX_ERRORTYPE error = OMX_FreeBuffer(componentHandle, outputPort, outputBufferHeader);
223 #pragma GCC diagnostic pop
224 outputBufferHeader = NULL;
225 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer in OMX_Image_Decode::deallocateOutputBuffer()", error);
228 void OMX_Image_Decode::sendToInput()
230 // log->debug(TAG, "Starting sendToInput");
231 inBuffer1->nFilledLen = dataSize;
232 inBuffer1->nOffset = 0;
233 inBuffer1->nTimeStamp = { 0, 0 };
234 inBuffer1->pAppPrivate = static_cast<OMX_PTR>(0);
235 inBuffer1->nFlags |= OMX_BUFFERFLAG_EOS;
237 // log->debug(TAG, "calling emptythisbuffer");
238 #pragma GCC diagnostic push
239 #pragma GCC diagnostic ignored "-Wold-style-cast"
240 OMX_ERRORTYPE error = OMX_EmptyThisBuffer(componentHandle, inBuffer1);
241 #pragma GCC diagnostic pop
242 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_EmptyThisBuffer in OMX_Image_Decode::sendToInput()", error);
243 // log->debug(TAG, "sendToInput done");
246 void OMX_Image_Decode::waitForOutputPortSettingsChange()
248 eventsProcessorMutex.lock();
249 std::unique_lock<std::mutex> ul(eventWaitersMutex);
251 EventWaiter* eventWaiter = new EventWaiter();
252 eventWaiter->waiting = true;
253 eventWaiter->eventType = OMX_EventPortSettingsChanged;
254 eventWaiter->port = outputPort;
255 eventWaiters.push_back(eventWaiter);
257 eventsProcessorWake = true;
258 eventsProcessorMutex.unlock();
259 eventsProcessorCond.notify_one();
261 eventWaiter->cond.wait(ul); // sleep this thread
264 if (!eventWaiter->receivedEvent)
267 throw OMX_Exception("Error waiting for event in waitForOutputPortSettingsChange", 0);
270 // The event processor thread received an event and saved it here
271 // log->debug(TAG, "received outputportsettingschanged event!");
272 delete eventWaiter->receivedEvent;
275 memset(&outputPortSettings, 0, sizeof(outputPortSettings));
276 outputPortSettings.nSize = sizeof(outputPortSettings);
277 outputPortSettings.nVersion.nVersion = OMX_VERSION;
278 outputPortSettings.nPortIndex = outputPort;
279 #pragma GCC diagnostic push
280 #pragma GCC diagnostic ignored "-Wold-style-cast"
281 OMX_ERRORTYPE error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &outputPortSettings);
282 #pragma GCC diagnostic pop
283 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_Image_Decode::waitForOutputPortSettingsChange()", error);
286 log->debug(TAG, "Port def type from outputPort");
287 log->debug(TAG, "buffer count actual: {}", outputPortSettings.nBufferCountActual);
288 log->debug(TAG, "eDir: {}", outputPortSettings.eDir);
289 log->debug(TAG, "nBufferSize: {}", outputPortSettings.nBufferSize);
290 log->debug(TAG, "bEnabled: {}", outputPortSettings.bEnabled);
291 log->debug(TAG, "bPopulated: {}", outputPortSettings.bPopulated);
292 log->debug(TAG, "eDomain: {}", outputPortSettings.eDomain);
294 log->debug(TAG, "cMIMEType: {}", static_cast<void*>(outputPortSettings.format.image.cMIMEType));
295 log->debug(TAG, "pNativeRender: {}", static_cast<void*>(outputPortSettings.format.image.pNativeRender));
296 log->debug(TAG, "nFrameWidth: {}", outputPortSettings.format.image.nFrameWidth);
297 log->debug(TAG, "nFrameHeight: {}", outputPortSettings.format.image.nFrameHeight);
298 log->debug(TAG, "nStride: {}", outputPortSettings.format.image.nStride);
299 log->debug(TAG, "nSliceHeight: {}", outputPortSettings.format.image.nSliceHeight);
303 void OMX_Image_Decode::receiveFromOutput(char** data, int* nFlags)
305 void* outputBufferMem{};
309 outputBufferMem = malloc(outputPortSettings.nBufferSize);
310 memset(outputBufferMem, 0, outputPortSettings.nBufferSize);
311 outputBufferHeader->pBuffer = static_cast<OMX_U8*>(outputBufferMem);
313 struct BufferWithOutputPort* bufferWithOutputPort = new BufferWithOutputPort();
314 bufferWithOutputPort->bufhead = outputBufferHeader;
315 outputBufferHeader->pAppPrivate = static_cast<OMX_PTR>(bufferWithOutputPort);
317 std::unique_lock<std::mutex> ul(bufferWithOutputPort->mutex);
319 // log->debug(TAG, "calling fillthisbuffer. bufhead: {}, BufferWithOutput: {}", static_cast<void*>(outputBufferHeader), static_cast<void*>(bufferWithOutputPort));
323 #pragma GCC diagnostic push
324 #pragma GCC diagnostic ignored "-Wold-style-cast"
325 error = OMX_FillThisBuffer(componentHandle, outputBufferHeader);
326 #pragma GCC diagnostic pop
327 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FillThisBuffer in OMX_Image_Decode::receiveFromOutput()", error);
329 // log->debug(TAG, "called fillthisbuffer. bufsize = {}", outputPortSettings.nBufferSize);
331 bufferWithOutputPort->cond.wait(ul, [bufferWithOutputPort] { return bufferWithOutputPort->done; });
334 // log->debug(TAG, "receiveFromOutputPort signalled");
336 *data = static_cast<char*>(outputBufferMem); // caller takes ownership
337 *nFlags = outputBufferHeader->nFlags;
339 outputBufferMem = NULL;
341 // The slice height is all important - can't trust nFilledLen
344 log->debug(TAG, "receiveFromOutput:");
345 log->debug(TAG, " nSize = {}", outputBufferHeader->nSize);
346 log->debug(TAG, " nAllocLen = {}", outputBufferHeader->nAllocLen);
347 log->debug(TAG, " nFilledLen = {}", outputBufferHeader->nFilledLen);
348 log->debug(TAG, " nOffset = {}", outputBufferHeader->nOffset);
349 log->debug(TAG, " nInputPortIndex = {}", outputBufferHeader->nInputPortIndex);
350 log->debug(TAG, " nOutputPortIndex = {}", outputBufferHeader->nOutputPortIndex);
351 log->debug(TAG, " nFlags = {:#x}", outputBufferHeader->nFlags);
354 delete bufferWithOutputPort;
360 free(outputBufferMem);
361 outputBufferMem = NULL;
367 void OMX_Image_Decode::getImageInfo(int* width, int* height, int* stride, int* sliceHeight)
369 *width = outputPortSettings.format.image.nFrameWidth;
370 *height = outputPortSettings.format.image.nFrameHeight;
371 *stride = outputPortSettings.format.image.nStride;
372 *sliceHeight = outputPortSettings.format.image.nSliceHeight;
375 OMX_ERRORTYPE OMX_Image_Decode::cb_EmptyBufferDone(OMX_IN OMX_HANDLETYPE /*hcomp*/, OMX_IN OMX_PTR /*appdata*/, OMX_IN OMX_BUFFERHEADERTYPE* /*buffer*/)
378 log->debug(TAG, "CB: EmptyBufferDone");
379 log->debug(TAG, " nSize = {}", buffer->nSize);
380 log->debug(TAG, " nAllocLen = {}", buffer->nAllocLen);
381 log->debug(TAG, " nFilledLen = {}", buffer->nFilledLen);
382 log->debug(TAG, " nOffset = {}", buffer->nOffset);
383 log->debug(TAG, " nInputPortIndex = {}", buffer->nInputPortIndex);
384 log->debug(TAG, " nOutputPortIndex = {}", buffer->nOutputPortIndex);
385 log->debug(TAG, " nFlags = {:#x}", buffer->nFlags);
387 return OMX_ErrorNone;
390 OMX_ERRORTYPE OMX_Image_Decode::cb_FillBufferDone(OMX_IN OMX_HANDLETYPE /*hcomp*/, OMX_IN OMX_PTR /*appdata*/, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
392 // log->debug(TAG, "CB: FillBufferDone, handle: {}, appdata: {}, buffer: {}", static_cast<void*>(hcomp), static_cast<void*>(appdata), static_cast<void*>(buffer));
394 struct BufferWithOutputPort* bufferWithOutputPort = static_cast<struct BufferWithOutputPort*>(buffer->pAppPrivate);
395 bufferWithOutputPort->mutex.lock();
396 bufferWithOutputPort->done = true;
397 bufferWithOutputPort->mutex.unlock();
398 bufferWithOutputPort->cond.notify_one();
400 return OMX_ErrorNone;