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/>.
23 #include "omx/omximagedecode.h"
24 #include "omx/omxeglrender.h"
25 #include "eglpicturecreator.h"
26 #include "imageloader.h"
28 #include "imageomx2.h"
30 const static char* TAG = "ImageOMX2";
32 ImageOMX2::ImageOMX2(/*EGLDisplay t_egl_display*/)
34 log = LogNT::getInstance();
35 //egl_display = t_egl_display;
38 void ImageOMX2::init()
40 omx_imagedecode = new OMX_Image_Decode();
41 if (!omx_imagedecode->init())
43 delete omx_imagedecode;
44 omx_imagedecode = NULL;
45 log->crit(TAG, "omx_imagedecode failed to init");
48 omx_eglrender = new OMX_EGL_Render();
49 if (!omx_eglrender->init())
51 delete omx_imagedecode;
53 omx_imagedecode = NULL;
55 log->crit(TAG, "omx_eglrender failed to init");
59 void ImageOMX2::shutdown()
61 if (omx_imagedecode) omx_imagedecode->shutdown();
62 if (omx_eglrender) omx_eglrender->shutdown();
63 delete omx_imagedecode;
65 omx_imagedecode = NULL;
69 void ImageOMX2::reinit()
71 // Only used from main exception handler in decodePicture. *Should* reset everything back to normal
76 unsigned char* ImageOMX2::decodePicture(LoadingIndex index, unsigned char* buffer, unsigned int length, bool freemem) noexcept
78 // This function needs to return NULL if successful, buffer if not.
84 if (!omx_imagedecode) throw 1;
85 if (!omx_eglrender) throw 2;
86 if (currentDecodeValid) throw 3;
87 if (length < 3) throw 4;
88 if ((buffer[0] != 0x89) || (buffer[1] != 0x50) || (buffer[2] != 0x4e)) throw 5;
90 currentDecodeValid = true;
92 currentDecode.lindex = index;
93 currentDecode.decoder = this;
94 currentDecode.type = PictureInfo::RGBAMemBlock;
95 //currentDecode.image/handle = // filled during render
96 //currentDecode.reference = // filled during render
103 decode(reinterpret_cast<char*>(buffer), length, &rawData, &rawDataSize, &rawDataWidth, &rawDataHeight, &rawDataStride);
105 // log->debug(TAG, "decoded {}, size = {}, w = {}, h = {}, s = {}", static_cast<void*>(rawData), rawDataSize, rawDataWidth, rawDataHeight, rawDataStride);
107 currentDecode.width = rawDataWidth;
108 currentDecode.height = rawDataHeight;
110 render(rawData, rawDataSize, rawDataWidth, rawDataHeight, rawDataStride);
113 // By the interface, if freemem = true, free buffer before returning
114 if (freemem) free(buffer);
121 case 1: log->error(TAG, "omx_imagedecode not initted"); break;
122 case 2: log->error(TAG, "omx_eglrender not initted"); break;
123 case 3: log->error(TAG, "Program error, currentDecodeValid = true on call to decodePicture"); break;
124 case 4: log->error(TAG, "Data length too short"); break;
125 case 5: log->error(TAG, "Input data not in PNG format"); break;
126 case 101: log->error(TAG, "Received all image from image_decode but error with nFlags and EOS"); break;
127 case 201: log->error(TAG, "Could not get egl picture creator"); break;
128 case 202: log->error(TAG, "Create egl picture failed"); break;
131 if (rawData) free(rawData);
135 catch (OMX_Exception& e)
137 log->error(TAG, "OMX Exception");
138 log->error(TAG, "{:#x} - {}", e.errorCode(), e.what());
140 if (rawData) free(rawData);
146 log->error(TAG, "Other exception caught in decodePicture?!");
148 if (rawData) free(rawData);
154 bool ImageOMX2::getDecodedPicture(struct PictureInfo& pict_inf)
156 if (!currentDecodeValid) return false;
157 pict_inf = currentDecode;
158 currentDecodeValid = false;
159 memset(¤tDecode, 0, sizeof(currentDecode));
163 // Internal functions
165 void ImageOMX2::decode(char* inputData, int inputDataSize, char** outRawData, int* outRawDataSize, int* outWidth, int* outHeight, int* outStride)
167 std::vector<char*> chunks;
168 std::vector<int> sizes;
172 // Talk to image_decode
174 // log->debug(TAG, "image_decode: disable input");
175 omx_imagedecode->disableInput();
176 // log->debug(TAG, "image_decode: disable output");
177 omx_imagedecode->disableOutput();
178 // log->debug(TAG, "image_decode: change state to idle");
179 omx_imagedecode->changeState(OMX_StateIdle);
180 // log->debug(TAG, "image_decode: set format");
181 omx_imagedecode->setFormat();
182 // log->debug(TAG, "image_decode: prepare input buffers");
183 omx_imagedecode->prepareInputBuffers(inputDataSize);
184 // log->debug(TAG, "image_decode: enable input");
185 omx_imagedecode->enableInput(false); // Don't wait for this, it depends on OMX_UseBuffer being called next
186 // log->debug(TAG, "image_decode: allocate input buffers");
187 omx_imagedecode->allocateInputBuffers(inputData);
189 // log->debug(TAG, "image_decode: change state to executing");
190 omx_imagedecode->changeState(OMX_StateExecuting);
192 // log->debug(TAG, "image_decode: sendtoinput");
193 omx_imagedecode->sendToInput();
194 // log->debug(TAG, "image_decode: wait for port settings change");
195 omx_imagedecode->waitForOutputPortSettingsChange();
196 // log->debug(TAG, "image_decode: fix slice height");
197 omx_imagedecode->setSliceHeight(16); // FIXME allow full height if imageHeight % 16 = 0 ??
198 // log->debug(TAG, "image_decode: enable output");
199 omx_imagedecode->enableOutput(false); // Don't wait for this, I hope it depends on OMX_UseBuffer being called next
200 // log->debug(TAG, "image_decode: allocate output buffer");
201 omx_imagedecode->allocateOutputBuffer();
208 omx_imagedecode->getImageInfo(&xwidth, &xheight, &xstride, &xsliceHeight);
209 // log->debug(TAG, "ImageInfo: {} {} {} {}", xwidth, xheight, xstride, xsliceHeight);
217 // log->debug(TAG, "image_decode: receiveFromOutput");
218 omx_imagedecode->receiveFromOutput(&data, &nFlags);
219 // log->debug(TAG, "image_decode: receiveFromOutput {:#x}", nFlags);
222 linesToGet = xheight - linesGot;
223 if (linesToGet > xsliceHeight) linesToGet = xsliceHeight;
225 chunks.push_back(data);
226 sizes.push_back(linesToGet * xstride);
228 linesGot += linesToGet;
230 if (linesGot == xheight)
232 if ((nFlags & OMX_BUFFERFLAG_EOS) == 0) throw 101;
237 // log->debug(TAG, "image_decode: change state to idle");
238 omx_imagedecode->changeState(OMX_StateIdle);
239 // log->debug(TAG, "image_decode: disable input");
240 omx_imagedecode->disableInput(false);
241 // log->debug(TAG, "image_decode: disable output");
242 omx_imagedecode->disableOutput(false);
243 // log->debug(TAG, "image_decode: deallocate input buffers");
244 omx_imagedecode->deallocateInputBuffers();
245 // log->debug(TAG, "image_decode: deallocate output buffer");
246 omx_imagedecode->deallocateOutputBuffer();
247 // log->debug(TAG, "image_decode: change state to loaded");
248 omx_imagedecode->changeState(OMX_StateLoaded);
251 for (int chunkSize : sizes) totalSize += chunkSize;
252 // log->debug(TAG, "total decoded mem chunks size: {}", totalSize);
253 char* gluedTogether = static_cast<char*>(malloc(totalSize));
254 // log->debug(TAG, "gluedTogether {}", static_cast<void*>(gluedTogether));
255 char* dest = gluedTogether;
256 for (unsigned int i = 0 ; i < chunks.size(); i++)
258 memcpy(dest, chunks[i], sizes[i]);
262 for(char* chunk : chunks) free(chunk);
264 *outRawDataSize = totalSize;
265 *outRawData = gluedTogether;
267 *outHeight = xheight;
268 *outStride = xstride;
272 for(char* chunk : chunks) free(chunk);
277 void ImageOMX2::render(char* inputData, int inputDataSize, int imageWidth, int imageHeight, int imageStride)
279 // log->debug(TAG, "render: inputdataSize = {}", inputDataSize);
281 EGLPictureCreator* pictcreat = dynamic_cast<EGLPictureCreator*>(Osd::getInstance());
282 if (!pictcreat) throw 201;
283 EGLDisplay egl_display2;
284 if (!pictcreat->getEGLPicture(currentDecode, &egl_display2)) throw 202;
286 // pictcreat must read width, height from currentDecode
287 // The VGImage handle returned from vgCreateImage is stored in currentDecode.handle
288 // The EGLImageKHR returned from eglCreateImageKHR is stored in currentDecode.reference
290 // log->debug(TAG, "egl_render: disable input");
291 omx_eglrender->disableInput();
292 // log->debug(TAG, "egl_render: disable output");
293 omx_eglrender->disableOutput();
294 // log->debug(TAG, "egl_render: prep");
295 omx_eglrender->prepareOutputPort(egl_display2);
296 // log->debug(TAG, "egl_render: change state to idle");
297 omx_eglrender->changeState(OMX_StateIdle);
298 // log->debug(TAG, "egl_render: prepareInputPort");
299 omx_eglrender->prepareInputPort(imageWidth, imageHeight, imageStride);
300 // log->debug(TAG, "egl_render: enable input");
301 omx_eglrender->enableInput(false);
302 // log->debug(TAG, "egl_render: allocate input buffers");
303 omx_eglrender->allocateInputBuffers(inputData);
304 // log->debug(TAG, "Print port settings");
305 // omx_eglrender->printPortSettings(false);
306 // log->debug(TAG, "egl_render: enable output");
307 omx_eglrender->enableOutput(false);
308 // log->debug(TAG, "egl_render: allocate output buffer");
309 omx_eglrender->allocateEGLImageKHR(currentDecode.reference);
310 // log->debug(TAG, "egl_render: change state to executing");
311 omx_eglrender->changeState(OMX_StateExecuting);
312 // log->debug(TAG, "egl_render: sendtoinput");
313 omx_eglrender->sendToInput(inputData, inputDataSize);
315 // omx_eglrender->printPortSettings(true);
317 // log->debug(TAG, "egl_render: render");
318 omx_eglrender->render();
319 // log->debug(TAG, "egl_render: render done");
322 // log->debug(TAG, "egl_render: change state to idle");
323 omx_eglrender->changeState(OMX_StateIdle);
325 // log->debug(TAG, "egl_render: flush input commands");
326 // omx_eglrender->flushInputCommands();
327 // log->debug(TAG, "egl_render: flush output commands");
328 // omx_eglrender->flushOutputCommands();
329 // log->debug(TAG, "egl_render: disable input");
330 omx_eglrender->disableInput(false);
331 // log->debug(TAG, "egl_render: disable output");
332 omx_eglrender->disableOutput(false);
333 // log->debug(TAG, "egl_render: deallocate output buffer header");
334 omx_eglrender->deallocateEGLImageKHR();
335 // log->debug(TAG, "egl_render: deallocate input buffer header");
336 omx_eglrender->deallocateInputBuffers();
337 // log->debug(TAG, "egl_render: change state to loaded");
338 omx_eglrender->changeState(OMX_StateLoaded);