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/>.
21 #include "omximagedecode.h"
22 #include "omxeglrender.h"
26 //static const char* TAG = "OMX";
29 // Static variable storage
30 OMX_Image_Decode* OMX::omx_image_decode{};
31 OMX_EGL_Render* OMX::omx_egl_render{};
33 OMX_HANDLETYPE OMX::handle_image_decode{};
34 OMX_HANDLETYPE OMX::handle_egl_render{};
36 std::thread OMX::eventsProcessorThread;
37 std::mutex OMX::eventsProcessorMutex;
38 std::condition_variable OMX::eventsProcessorCond;
39 std::mutex OMX::eventsMutex;
41 bool OMX::eventsProcessorWake{};
42 bool OMX::eventsProcessorShutdownNow{};
43 int OMX::eventsProcessorUsageCount{};
44 EventWaiters OMX::eventWaiters;
45 std::mutex OMX::eventWaitersMutex;
48 // Main static callbacks
50 OMX_ERRORTYPE OMX::scb_EventHandler(
51 OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_EVENTTYPE event_type,
52 OMX_IN OMX_U32 data1, OMX_IN OMX_U32 data2, OMX_IN OMX_PTR event_data)
54 // LogNT::getInstance()->debug("OMXSCB", "EventHandler {} {} {} {}", static_cast<void*>(handle_image_decode), static_cast<void*>(omx_image_decode), static_cast<void*>(handle_egl_render), static_cast<void*>(omx_egl_render));
55 if (handle == handle_image_decode)
56 return omx_image_decode->cb_EventHandler(handle, appdata, event_type, data1, data2, event_data);
57 else if (handle == handle_egl_render)
58 return omx_egl_render->cb_EventHandler(handle, appdata, event_type, data1, data2, event_data);
63 OMX_ERRORTYPE OMX::scb_EmptyBufferDone(OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
65 // LogNT::getInstance()->debug("OMXSCB", "EmptyBufferDone {} {} {} {}", static_cast<void*>(handle_image_decode), static_cast<void*>(omx_image_decode), static_cast<void*>(handle_egl_render), static_cast<void*>(omx_egl_render));
66 if (handle == handle_image_decode)
67 return omx_image_decode->cb_EmptyBufferDone(handle, appdata, buffer);
68 else if (handle == handle_egl_render)
69 return omx_egl_render->cb_EmptyBufferDone(handle, appdata, buffer);
74 OMX_ERRORTYPE OMX::scb_FillBufferDone(OMX_IN OMX_HANDLETYPE handle, OMX_IN OMX_PTR appdata, OMX_IN OMX_BUFFERHEADERTYPE* buffer)
76 // LogNT::getInstance()->debug("OMXSCB", "FillBufferDone {} {} {} {}", static_cast<void*>(handle_image_decode), static_cast<void*>(omx_image_decode), static_cast<void*>(handle_egl_render), static_cast<void*>(omx_egl_render));
77 if (handle == handle_image_decode)
78 return omx_image_decode->cb_FillBufferDone(handle, appdata, buffer);
79 else if (handle == handle_egl_render)
80 return omx_egl_render->cb_FillBufferDone(handle, appdata, buffer);
88 log = LogNT::getInstance();
95 // bool OMX::OMX_Master_Init()
97 // OMX_ERRORTYPE error;
98 // error = OMX_Init();
99 // if (error != OMX_ErrorNone) return false;
103 OMX_ERRORTYPE OMX::cb_EventHandler(OMX_IN OMX_HANDLETYPE /* handle */, OMX_IN OMX_PTR appdata, OMX_IN OMX_EVENTTYPE event_type, OMX_IN OMX_U32 data1, OMX_IN OMX_U32 data2, OMX_IN OMX_PTR event_data)
105 //Log* l = Log::getInstance();
106 // log->debug(TAG, "CB: eventHandler {} {} {:#x} {:#x} {}", static_cast<void*>(handle), event_type, data1, data2, static_cast<void*>(event_data));
109 Event* incomingEvent = new Event{.appdata = appdata, .eventType = event_type, .data1 = data1, .data2 = data2, .event_data = event_data};
110 events.push_back(incomingEvent);
111 eventsMutex.unlock();
113 eventsProcessorMutex.lock();
114 eventsProcessorWake = true;
115 eventsProcessorMutex.unlock();
116 eventsProcessorCond.notify_one();
118 // log->debug(TAG, "eventHandler end");
119 return OMX_ErrorNone;
122 void OMX::initEventsProcessing()
124 eventsProcessorMutex.lock();
126 if (eventsProcessorUsageCount == 0)
128 eventsProcessorUsageCount = 1;
130 eventsProcessorThread = std::thread( [&]
132 eventsProcessorMutex.lock();
133 eventsProcessorMutex.unlock();
134 eventsProcessorLoop();
139 ++eventsProcessorUsageCount;
142 eventsProcessorMutex.unlock();
145 void OMX::stopEventsProcessing()
147 eventsProcessorMutex.lock();
149 --eventsProcessorUsageCount;
151 if (eventsProcessorUsageCount == 0)
154 eventsProcessorWake = true;
155 eventsProcessorShutdownNow = true;
156 eventsProcessorCond.notify_one();
157 eventsProcessorMutex.unlock();
158 eventsProcessorThread.join();
159 // eventsProcessorShutdownNow = false; // This is the fix.
161 // But where else triggers the bug?
167 eventsProcessorMutex.unlock();
171 void OMX::eventsProcessorLoop()
173 //LogNT* log = LogNT::getInstance();
174 std::unique_lock<std::mutex> ul(eventsProcessorMutex); // locks
179 if (eventsProcessorWake)
181 eventsProcessorWake = false;
184 if (eventsProcessorShutdownNow) return;
187 for (Events::iterator eventIterator = events.begin(); eventIterator != events.end(); ) // for each event received from OMX
189 Event* event = *eventIterator;
193 event->isNew = false;
196 log->debug(TAG, "Event Processor Loop - event:");
198 if (event->eventType == 0)
199 log->debug(TAG, "* eventType: OMX_EventCmdComplete");
200 else if (event->eventType == 1)
201 log->debug(TAG, "* eventType: OMX_EventError");
202 else if (event->eventType == 2)
203 log->debug(TAG, "* eventType: OMX_EventMark");
204 else if (event->eventType == 3)
205 log->debug(TAG, "* eventType: OMX_EventPortSettingsChanged");
206 else if (event->eventType == 4)
207 log->debug(TAG, "* eventType: OMX_EventBufferFlag");
209 log->debug(TAG, "* eventType: {:#x}", event->eventType);
211 if (event->eventType == OMX_EventCmdComplete)
213 if (event->data1 == OMX_CommandStateSet)
214 log->debug(TAG, "* OMX_COMMANDTYPE: OMX_CommandStateSet");
215 if (event->data1 == OMX_CommandFlush)
216 log->debug(TAG, "* OMX_COMMANDTYPE: OMX_CommandFlush");
217 if (event->data1 == OMX_CommandPortDisable)
218 log->debug(TAG, "* OMX_COMMANDTYPE: OMX_CommandPortDisable");
219 if (event->data1 == OMX_CommandPortEnable)
220 log->debug(TAG, "* OMX_COMMANDTYPE: OMX_CommandPortEnable");
221 if (event->data1 == OMX_CommandMarkBuffer)
222 log->debug(TAG, "* OMX_COMMANDTYPE: OMX_CommandMarkBuffer");
224 if (event->data1 == OMX_CommandStateSet)
226 log->debug(TAG, "* new state: {}", event->data2);
229 if ( (event->data1 == OMX_CommandPortEnable) || (event->data1 == OMX_CommandPortDisable) )
231 log->debug(TAG, "* port en/disabled: {}", event->data2);
234 else if (event->eventType == OMX_EventBufferFlag)
236 log->debug(TAG, "* Port: {:#x} {}", event->data1, event->data1);
238 else if (event->eventType == OMX_EventError)
240 log->debug(TAG, "* Error code: {:#x}", event->data1);
242 if (event->data1 == OMX_ErrorPortUnpopulated)
243 log->debug(TAG, "* = : OMX_ErrorPortUnpopulated");
244 if (event->data1 == OMX_ErrorInsufficientResources)
245 log->debug(TAG, "* =: OMX_ErrorInsufficientResources");
249 } // end if event is new (then print it out)
252 // Find matching EventWaiter
254 eventWaitersMutex.lock();
256 // Now have EventsMutex and eventWaitersMutex
258 EventWaiters::iterator eventWaiterIterator;
259 EventWaiter* eventWaiter{};
261 for (eventWaiterIterator = eventWaiters.begin(); eventWaiterIterator < eventWaiters.end(); eventWaiterIterator++)
263 // structure here not great yet
264 // not looking at eventWaiter->eventType??
266 eventWaiter = *eventWaiterIterator;
268 if (event->eventType == OMX_EventCmdComplete)
270 if ( ((event->data1 == OMX_CommandPortEnable) && (eventWaiter->command == OMX_CommandPortEnable))
271 || ((event->data1 == OMX_CommandPortDisable) && (eventWaiter->command == OMX_CommandPortDisable)) )
273 if (event->data2 == eventWaiter->port)
276 eventWaiter->receivedEvent = event;
280 else if ((event->data1 == OMX_CommandStateSet) && (eventWaiter->command == OMX_CommandStateSet))
282 if (event->data2 == eventWaiter->newState)
285 eventWaiter->receivedEvent = event;
289 else if ((event->data1 == OMX_CommandFlush) && (eventWaiter->command == OMX_CommandFlush))
291 if (event->data2 == eventWaiter->flushPort)
294 eventWaiter->receivedEvent = event;
299 else if (event->eventType == OMX_EventPortSettingsChanged)
301 if ( (eventWaiter->eventType == OMX_EventPortSettingsChanged)
302 && (event->data1 == eventWaiter->port) )
305 eventWaiter->receivedEvent = event;
309 } // end of each eventWaiter loop
312 // Now obv we have an event, but do we have an eventWaiter? If not, advance and continue
313 if (!eventWaiter || (eventWaiterIterator == eventWaiters.end())) // A) none in the loop at all, B) none matched
315 eventWaitersMutex.unlock();
321 // Now we have an event (event, eventIterator) and a found eventWaiter (eventWaiter, eventWaiterIterator)
323 // Remove the eventWaiter from the deque
324 eventWaiters.erase(eventWaiterIterator);
325 eventWaitersMutex.unlock();
327 // Remove the event from the deque
328 eventIterator = events.erase(eventIterator);
330 // Iterators are now dead.
331 // Now we have an event and an eventWaiter
332 // Still have the EventsMutex but we have released the eventWaitersMutex.
334 // log->debug(TAG, "This event {} found an event waiter for it {}", static_cast<void*>(event), static_cast<void*>(eventWaiter));
337 if (eventWaiter->waiting)
339 // FIXME look at whether eventWaiter could miss this signal
340 // log->debug(TAG, "Notifying eventWaiter: {} on cond {}", static_cast<void*>(eventWaiter), static_cast<void*>(&eventWaiter->cond));
341 eventWaiter->cond.notify_one();
345 // log->debug(TAG, "EventWaiter not waiting. Deleting {}", static_cast<void*>(eventWaiter));
350 // log->debug(TAG, "Done processing eventWaiter, goaround events loop");
353 } // end for each received event
355 // FIXME If there are events still in events that have not been matched up and sent
356 // to an eventWaiter, or that were not marked as waiting so we deleted it here, then this is a bug
357 eventsMutex.unlock();
359 // Reacquire eventsProcessor lock
361 } // end if event processor wake
365 eventsProcessorCond.wait(ul, [&] { return eventsProcessorWake; });
371 void OMX::enablePort(OMX_U32 port, bool enable, bool wait)
375 OMX_PARAM_PORTDEFINITIONTYPE pdt;
376 memset(&pdt, 0, sizeof(pdt));
377 pdt.nSize = sizeof(pdt);
378 pdt.nVersion.nVersion = OMX_VERSION;
379 pdt.nPortIndex = port;
380 #pragma GCC diagnostic push
381 #pragma GCC diagnostic ignored "-Wold-style-cast"
382 error = OMX_GetParameter(componentHandle, OMX_IndexParamPortDefinition, &pdt);
383 #pragma GCC diagnostic pop
384 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_GetParameter in OMX::enablePort()", error);
386 if (pdt.bEnabled == enable) return; // already in requested state
388 std::unique_lock<std::mutex> ul(eventWaitersMutex);
390 OMX_COMMANDTYPE command = enable ? OMX_CommandPortEnable : OMX_CommandPortDisable;
392 // log->debug(TAG, "en/disablePort: port: {:#x}, command: {:#x}", port, command);
393 #pragma GCC diagnostic push
394 #pragma GCC diagnostic ignored "-Wold-style-cast"
395 error = OMX_SendCommand(componentHandle, command, port, 0);
396 #pragma GCC diagnostic pop
397 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SendCommand in OMX::enablePort()", error);
398 // log->debug(TAG, "en/disablePort: port: {:#x}, command: {:#x} done", port, command);
400 EventWaiter* eventWaiter = new EventWaiter();
401 eventWaiter->waiting = wait;
402 eventWaiter->command = command;
403 eventWaiter->port = port;
405 eventWaiters.push_back(eventWaiter);
407 if (!wait) return; // && unlock. eventWaiter becomes owned by eventProcessor thread
409 // log->debug(TAG, "en/disablePort: Going to wait on cond {}", static_cast<void*>(&eventWaiter->cond));
410 eventWaiter->cond.wait(ul); // sleep this thread
411 // log->debug(TAG, "en/disablePort: Back from wait on cond {}", static_cast<void*>(&eventWaiter->cond));
415 if (eventWaiter->receivedEvent)
417 // The event processor thread received an event and saved it here
418 // log->debug(TAG, "received event!");
419 delete eventWaiter->receivedEvent;
424 delete eventWaiter; // This is ours, at least we can clean this up
425 throw OMX_Exception("Failed to receive event!", 0);
429 void OMX::changeState(OMX_STATETYPE reqState, bool wait)
431 OMX_STATETYPE currentState;
432 #pragma GCC diagnostic push
433 #pragma GCC diagnostic ignored "-Wold-style-cast"
434 OMX_GetState(componentHandle, ¤tState);
435 #pragma GCC diagnostic pop
436 // log->debug(TAG, "Current state: {}", currentState);
437 if (currentState == reqState)
439 // log->debug(TAG, "changeState return immediately, already in reqState");
443 std::unique_lock<std::mutex> ul(eventWaitersMutex);
444 #pragma GCC diagnostic push
445 #pragma GCC diagnostic ignored "-Wold-style-cast"
446 OMX_ERRORTYPE error = OMX_SendCommand(componentHandle, OMX_CommandStateSet, reqState, 0);
447 #pragma GCC diagnostic pop
448 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SendCommand in OMX::changeState()", error);
450 EventWaiter* eventWaiter = new EventWaiter();
451 eventWaiter->waiting = wait;
452 eventWaiter->command = OMX_CommandStateSet;
453 eventWaiter->newState = reqState;
455 eventWaiters.push_back(eventWaiter);
457 if (!wait) return; // && unlock. eventWaiter becomes owned by eventProcessor thread
459 // log->debug(TAG, "changeState: Going to wait on cond {}", static_cast<void*>(&eventWaiter->cond));
460 eventWaiter->cond.wait(ul); // sleep this thread
461 // log->debug(TAG, "changeState: Back from wait on cond {}", static_cast<void*>(&eventWaiter->cond));
464 if (eventWaiter->receivedEvent)
466 // The event processor thread received an event and saved it here
467 // log->debug(TAG, "received event!");
468 delete eventWaiter->receivedEvent;
473 delete eventWaiter; // This is ours, at least we can clean this up
474 throw OMX_Exception("Failed to receive event!", 0);
478 void OMX::flushCommands(OMX_U32 port, bool wait)
480 std::unique_lock<std::mutex> ul(eventWaitersMutex);
482 #pragma GCC diagnostic push
483 #pragma GCC diagnostic ignored "-Wold-style-cast"
484 OMX_ERRORTYPE error = OMX_SendCommand(componentHandle, OMX_CommandFlush, port, NULL);
485 #pragma GCC diagnostic pop
486 if (error != OMX_ErrorNone) throw OMX_Exception("OMX_SendCommand in OMX::flushCommands()", error);
488 EventWaiter* eventWaiter = new EventWaiter();
489 eventWaiter->waiting = wait;
490 eventWaiter->command = OMX_CommandFlush;
491 eventWaiter->flushPort = port;
493 eventWaiters.push_back(eventWaiter);
495 if (!wait) return; // && unlock. eventWaiter becomes owned by eventProcessor thread
497 // log->debug(TAG, "flushCommands: Going to wait on cond {}", static_cast<void*>(&eventWaiter->cond));
498 eventWaiter->cond.wait(ul); // sleep this thread
499 // log->debug(TAG, "flushCommands: Back from wait on cond {}", static_cast<void*>(&eventWaiter->cond));
502 if (!eventWaiter->receivedEvent)
505 throw OMX_Exception("Error waiting for event in flushCommands", 0);
508 // The event processor thread received an event and saved it here
509 // log->debug(TAG, "received event!");
510 delete eventWaiter->receivedEvent;