]> git.vomp.tv Git - vompclient.git/blob - omx/omximagedecode.cc
Fix subs running during pause - after a jump then quick pause
[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   #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;
62
63   inputPort = p_param.nStartPortNumber;
64   outputPort = p_param.nStartPortNumber + 1;
65
66 //   log->debug(TAG, "IN: {}, OUT: {}", inputPort, outputPort);
67   return true;
68 }
69
70 void OMX_Image_Decode::shutdown()
71 {
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);
75 }
76
77 void OMX_Image_Decode::prepareInputBuffers(int tdataSize)
78 {
79   dataSize = tdataSize;
80
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;
86
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);
92
93 //   log->debug(TAG, "Defaults: bufferCountActual: {}, bufferCountMin: {}, nBufferSize: {}",
94 //         port_def_type.nBufferCountActual, port_def_type.nBufferCountMin, port_def_type.nBufferSize);
95
96   port_def_type.nBufferCountActual = port_def_type.nBufferCountMin;
97   if (dataSize > port_def_type.nBufferSize) port_def_type.nBufferSize = dataSize;
98
99 //   log->debug(TAG, "Setting buf count actual: {}, buf size: {}",
100 //         port_def_type.nBufferCountActual, port_def_type.nBufferSize);
101
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);
107 }
108
109 void OMX_Image_Decode::setFormat()
110 {
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;
117
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;
121
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");
128 }
129
130 void OMX_Image_Decode::setSliceHeight(int newSliceHeight)
131 {
132   outputPortSettings.format.image.nSliceHeight = newSliceHeight;
133 //   log->debug(TAG, "nSliceHeight: {}", outputPortSettings.format.image.nSliceHeight);
134
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);
140
141   /*
142   {
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;
149
150     error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &indTest);
151     if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX_Image_Decode::setSliceHeight()", error);
152
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);
160
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);
168   }
169   */
170 }
171
172 void OMX_Image_Decode::allocateInputBuffers(char* data)
173 {
174   inBuffer1 = NULL;
175   inBuffer2 = NULL;
176
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);
183
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);
190 }
191
192 void OMX_Image_Decode::allocateOutputBuffer()
193 {
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);
199 }
200
201 void OMX_Image_Decode::deallocateInputBuffers()
202 {
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
207   inBuffer1 = NULL;
208   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer 1 in OMX_Image_Decode::deallocateInputBuffers()", error);
209
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
214   inBuffer2 = NULL;
215   if (error != OMX_ErrorNone) throw OMX_Exception("OMX_FreeBuffer 2 in OMX_Image_Decode::deallocateInputBuffers()", error);
216 }
217
218 void OMX_Image_Decode::deallocateOutputBuffer()
219 {
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);
226 }
227
228 void OMX_Image_Decode::sendToInput()
229 {
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;
236
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");
244 }
245
246 void OMX_Image_Decode::waitForOutputPortSettingsChange()
247 {
248   eventsProcessorMutex.lock();
249   std::unique_lock<std::mutex> ul(eventWaitersMutex);
250
251   EventWaiter* eventWaiter = new EventWaiter();
252   eventWaiter->waiting = true;
253   eventWaiter->eventType = OMX_EventPortSettingsChanged;
254   eventWaiter->port = outputPort;
255   eventWaiters.push_back(eventWaiter);
256
257   eventsProcessorWake = true;
258   eventsProcessorMutex.unlock();
259   eventsProcessorCond.notify_one();
260
261   eventWaiter->cond.wait(ul);  // sleep this thread
262   ul.unlock();
263
264   if (!eventWaiter->receivedEvent)
265   {
266     delete eventWaiter;
267     throw OMX_Exception("Error waiting for event in waitForOutputPortSettingsChange", 0);
268   }
269
270   // The event processor thread received an event and saved it here
271 //   log->debug(TAG, "received outputportsettingschanged event!");
272   delete eventWaiter->receivedEvent;
273   delete eventWaiter;
274
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);
284
285 /*
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);
293
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);
300   */
301 }
302
303 void OMX_Image_Decode::receiveFromOutput(char** data, int* nFlags)
304 {
305   void* outputBufferMem{};
306
307   try
308   {
309     outputBufferMem = malloc(outputPortSettings.nBufferSize);
310     memset(outputBufferMem, 0, outputPortSettings.nBufferSize);
311     outputBufferHeader->pBuffer = static_cast<OMX_U8*>(outputBufferMem);
312
313     struct BufferWithOutputPort* bufferWithOutputPort = new BufferWithOutputPort();
314     bufferWithOutputPort->bufhead = outputBufferHeader;
315     outputBufferHeader->pAppPrivate = static_cast<OMX_PTR>(bufferWithOutputPort);
316
317     std::unique_lock<std::mutex> ul(bufferWithOutputPort->mutex);
318
319 //     log->debug(TAG, "calling fillthisbuffer. bufhead: {}, BufferWithOutput: {}", static_cast<void*>(outputBufferHeader), static_cast<void*>(bufferWithOutputPort));
320
321     OMX_ERRORTYPE error;
322
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);
328
329 //     log->debug(TAG, "called fillthisbuffer. bufsize = {}", outputPortSettings.nBufferSize);
330
331     bufferWithOutputPort->cond.wait(ul, [bufferWithOutputPort] { return bufferWithOutputPort->done; });
332     ul.unlock();
333
334 //     log->debug(TAG, "receiveFromOutputPort signalled");
335
336     *data = static_cast<char*>(outputBufferMem); // caller takes ownership
337     *nFlags = outputBufferHeader->nFlags;
338
339     outputBufferMem = NULL;
340
341     // The slice height is all important - can't trust nFilledLen
342
343     /*
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);
352     */
353
354     delete bufferWithOutputPort;
355   }
356   catch(...)
357   {
358     if (outputBufferMem)
359     {
360       free(outputBufferMem);
361       outputBufferMem = NULL;
362     }
363     throw;
364   }
365 }
366
367 void OMX_Image_Decode::getImageInfo(int* width, int* height, int* stride, int* sliceHeight)
368 {
369   *width = outputPortSettings.format.image.nFrameWidth;
370   *height = outputPortSettings.format.image.nFrameHeight;
371   *stride = outputPortSettings.format.image.nStride;
372   *sliceHeight = outputPortSettings.format.image.nSliceHeight;
373 }
374
375 OMX_ERRORTYPE OMX_Image_Decode::cb_EmptyBufferDone(OMX_IN OMX_HANDLETYPE /*hcomp*/, OMX_IN OMX_PTR /*appdata*/, OMX_IN OMX_BUFFERHEADERTYPE* /*buffer*/)
376 {
377   /*
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);
386   */
387   return OMX_ErrorNone;
388 }
389
390 OMX_ERRORTYPE OMX_Image_Decode::cb_FillBufferDone(OMX_IN OMX_HANDLETYPE /*hcomp*/, OMX_IN OMX_PTR /*appdata*/, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
391 {
392 //   log->debug(TAG, "CB: FillBufferDone, handle: {}, appdata: {}, buffer: {}", static_cast<void*>(hcomp), static_cast<void*>(appdata), static_cast<void*>(buffer));
393
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();
399
400   return OMX_ErrorNone;
401 }