]> git.vomp.tv Git - vompclient.git/blob - imageomx2.cc
Rewrite of ImageOMX to fix the PNG problem
[vompclient.git] / imageomx2.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 "osdvector.h"
21
22 #include "omx/omximagedecode.h"
23 #include "omx/omxeglrender.h"
24
25 #include "imageomx2.h"
26
27 #include "eglpicturecreator.h"
28
29 const static char* TAG = "ImageOMX2";
30
31 ImageOMX2::ImageOMX2(/*EGLDisplay t_egl_display*/)
32 : OsdVector::PictureDecoder(NULL)
33 {
34   log = LogNT::getInstance();
35   //egl_display = t_egl_display;
36 }
37
38 void ImageOMX2::init()
39 {
40   omx_imagedecode = new OMX_Image_Decode();
41   if (!omx_imagedecode->init())
42   {
43     delete omx_imagedecode;
44     omx_imagedecode = NULL;
45     log->crit(TAG, "omx_imagedecode failed to init");
46   }
47
48   omx_eglrender = new OMX_EGL_Render();
49   if (!omx_eglrender->init())
50   {
51     delete omx_imagedecode;
52     delete omx_eglrender;
53     omx_imagedecode = NULL;
54     omx_eglrender = NULL;
55     log->crit(TAG, "omx_eglrender failed to init");
56   }
57 }
58
59 void ImageOMX2::shutdown()
60 {
61   if (omx_imagedecode) omx_imagedecode->shutdown();
62   if (omx_eglrender) omx_eglrender->shutdown();
63   delete omx_imagedecode;
64   delete omx_eglrender;
65   omx_imagedecode = NULL;
66   omx_eglrender = NULL;
67 }
68
69 void ImageOMX2::reinit()
70 {
71   // Only used from main exception handler in decodePicture. *Should* reset everything back to normal
72   shutdown();
73   init();
74 }
75
76 unsigned char* ImageOMX2::decodePicture(LoadIndex index, unsigned char* buffer, unsigned int length, bool freemem) noexcept
77 {
78   // This function needs to return NULL if successful, buffer if not.
79
80   char* rawData{};
81
82   try
83   {
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;
89
90     currentDecodeValid = true;
91
92     currentDecode.lindex = index;
93     currentDecode.decoder = this;
94     currentDecode.type = OsdVector::PictureInfo::RGBAMemBlock;
95     //currentDecode.image/handle =   // filled during render
96     //currentDecode.reference =      // filled during render
97
98     int rawDataSize{};
99     int rawDataWidth{};
100     int rawDataHeight{};
101     int rawDataStride{};
102
103     decode(reinterpret_cast<char*>(buffer), length, &rawData, &rawDataSize, &rawDataWidth, &rawDataHeight, &rawDataStride);
104
105     log->debug(TAG, "decoded {}, size = {}, w = {}, h = {}, s = {}", static_cast<void*>(rawData), rawDataSize, rawDataWidth, rawDataHeight, rawDataStride);
106
107     currentDecode.width = rawDataWidth;
108     currentDecode.height = rawDataHeight;
109
110     render(rawData, rawDataSize, rawDataWidth, rawDataHeight, rawDataStride);
111     free(rawData);
112
113     // By the interface, if freemem = true, free buffer before returning
114     if (freemem) free(buffer);
115     return NULL;
116   }
117   catch (int e)
118   {
119     switch(e)
120     {
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;
129     }
130
131     if (rawData) free(rawData);
132     reinit();
133     return buffer;
134   }
135   catch (OMX_Exception& e)
136   {
137     log->error(TAG, "OMX Exception");
138     log->error(TAG, "{:#x} - {}", e.errorCode(), e.what());
139
140     if (rawData) free(rawData);
141     reinit();
142     return buffer;
143   }
144   catch (...)
145   {
146     log->error(TAG, "Other exception caught in decodePicture?!");
147
148     if (rawData) free(rawData);
149     reinit();
150     return buffer;
151   }
152 }
153
154 bool ImageOMX2::getDecodedPicture(struct OsdVector::PictureInfo& pict_inf)
155 {
156   if (!currentDecodeValid) return false;
157   pict_inf = currentDecode;
158   currentDecodeValid = false;
159   memset(&currentDecode, 0, sizeof(currentDecode));
160   return true;
161 }
162
163 // Internal functions
164
165 void ImageOMX2::decode(char* inputData, int inputDataSize, char** outRawData, int* outRawDataSize, int* outWidth, int* outHeight, int* outStride)
166 {
167   std::vector<char*> chunks;
168   std::vector<int> sizes;
169
170   try
171   {
172     // Talk to image_decode
173
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);
188
189     log->debug(TAG, "image_decode: change state to executing");
190     omx_imagedecode->changeState(OMX_StateExecuting);
191
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();
202
203     int xwidth{};
204     int xheight{};
205     int xstride{};
206     int xsliceHeight{};
207
208     omx_imagedecode->getImageInfo(&xwidth, &xheight, &xstride, &xsliceHeight);
209     log->debug(TAG, "ImageInfo: {} {} {} {}", xwidth, xheight, xstride, xsliceHeight);
210
211     int linesGot = 0;
212     int linesToGet;
213     while(1)
214     {
215       char* data;
216       int nFlags;
217       log->debug(TAG, "image_decode: receiveFromOutput");
218       omx_imagedecode->receiveFromOutput(&data, &nFlags);
219       log->debug(TAG, "image_decode: receiveFromOutput {:#x}", nFlags);
220
221
222       linesToGet = xheight - linesGot;
223       if (linesToGet > xsliceHeight) linesToGet = xsliceHeight;
224
225       chunks.push_back(data);
226       sizes.push_back(linesToGet * xstride);
227
228       linesGot += linesToGet;
229
230       if (linesGot == xheight)
231       {
232         if ((nFlags & OMX_BUFFERFLAG_EOS) == 0) throw 101;
233         break;
234       }
235     }
236
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);
249
250     int totalSize{};
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++)
257     {
258       memcpy(dest, chunks[i], sizes[i]);
259       dest += sizes[i];
260     }
261
262     for(char* chunk : chunks) free(chunk);
263
264     *outRawDataSize = totalSize;
265     *outRawData = gluedTogether;
266     *outWidth = xwidth;
267     *outHeight = xheight;
268     *outStride = xstride;
269   }
270   catch (...)
271   {
272     for(char* chunk : chunks) free(chunk);
273     throw;
274   }
275 }
276
277 void ImageOMX2::render(char* inputData, int inputDataSize, int imageWidth, int imageHeight, int imageStride)
278 {
279   log->debug(TAG, "render: inputdataSize = {}", inputDataSize);
280
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;
285
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
289
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);
314
315   omx_eglrender->printPortSettings(true);
316
317   log->debug(TAG, "egl_render: render");
318   omx_eglrender->render();
319   log->debug(TAG, "egl_render: render done");
320
321
322   log->debug(TAG, "egl_render: change state to idle");
323   omx_eglrender->changeState(OMX_StateIdle);
324
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);
339 }