vdr-plugin-softhddevice-drm-gles 1.6.8-daba64b
statemachine.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
16#include <atomic>
17#include <mutex>
18#include <variant>
19#include <vector>
20
21#include <vdr/thread.h>
22
23#include "logger.h"
24#include "softhddevice.h"
25#include "statemachine.h"
26
33 : m_pDevice(device)
34{
35}
36
44{
45 if (m_state == newState)
46 return;
47
48 LOGDEBUG("STATE MACHINE: Preparing to leave state %s", StateToString(m_state));
50 LOGDEBUG("STATE MACHINE: Changing state %s -> %s", StateToString(m_state), StateToString(newState));
53 LOGDEBUG("STATE MACHINE: State changed to %s", StateToString(m_state));
54}
55
67{
68 uint64_t startStateChange = cTimeMs::Now();
69 LOGDEBUG("STATE MACHINE: received %s", EventToString(event));
70 bool needsResume = false;
71
72#ifdef USE_GLES
73 // Lock the GL thread before the state machine lock, because cmdCopyBufferToOutputFb() calls
74 // cSoftHdDevice::OsdDrawARGB(), which itself locks the state machine mutex and we can end
75 // up in a deadlock then.
76 // We can safely unlock the thread again after the state change, because cSoftHdDevice::OsdDrawARGB()
77 // always tests if we are in detached mode and this new state is probably set then.
78 bool needsOglResume = false;
79 if (m_state != DETACHED)
81#endif
82
83 { // locked state machine context
84 std::lock_guard<std::mutex> lock(m_mutex);
85
86 if (m_state != DETACHED) {
88 needsResume = true;
89 }
90
91 auto invalid = [this, &event]() {
92 LOGWARNING("STATE MACHINE: Invalid event '%s' in state '%s' received", EventToString(event), StateToString(m_state));
93 };
94
95 switch (m_state) {
96 case State::DETACHED:
97 std::visit(overload{
98 [&invalid](const PlayEvent&) { invalid(); },
99 [&invalid](const PauseEvent&) { invalid(); },
100 [&invalid](const StopEvent&) { invalid(); },
101 [&invalid](const TrickSpeedEvent&) { invalid(); },
102 [&invalid](const StillPictureEvent&) { invalid(); },
103 [](const DetachEvent&) { /* ignore */ },
104 [this](const AttachEvent&) {
107 },
108 [&invalid](const BufferUnderrunEvent&) { invalid(); },
111 [&invalid](const ResyncEvent&) { invalid(); },
112 [&invalid](const DisplayChangeEvent&) { invalid(); },
113 }, event);
114 needsResume = false;
115 break;
116 case State::STOP:
117 std::visit(overload{
118 [this, &needsResume](const PlayEvent&) {
119 if (m_pDevice->InitAudio(true)) {
120 LOGERROR("device: audio init failed, change to detached mode");
123 needsResume = false;
124 } else {
127 }
128 },
129 [&invalid](const PauseEvent&) { invalid(); },
130 [&invalid](const StopEvent&) { invalid(); },
131 [&invalid](const TrickSpeedEvent&) { invalid(); },
132 [this](const StillPictureEvent& s) {
133 m_pDevice->HandleStillPicture(s.data, s.size);
134 },
135 [this, &needsResume](const DetachEvent&) {
137 needsResume = false;
138 },
139 [&invalid](const AttachEvent&) { invalid(); },
140 [&invalid](const BufferUnderrunEvent&) { invalid(); },
143 [&invalid](const ResyncEvent&) { invalid(); },
144 [this](const DisplayChangeEvent& d) {
146 },
147 }, event);
148 break;
149 case State::BUFFERING:
150 std::visit(overload{
151 [this](const PlayEvent&) { /* ignore */ },
152 [this](const PauseEvent&) { /* ignore */ },
153 [this](const StopEvent&) {
155 },
156 [this](const TrickSpeedEvent& t) {
157 // abort buffering and proceed with trick speed immediately, because trick speed shall be as fast and as demanded as possible
159 m_pDevice->SetTrickSpeed(t.speed, t.active, t.forward);
161 },
162 [this](const StillPictureEvent& s) {
163 m_pDevice->HandleStillPicture(s.data, s.size);
164 },
165 [this, &needsResume](const DetachEvent&) {
167 needsResume = false;
168 },
169 [&invalid](const AttachEvent&) { invalid(); },
170 [&invalid](const BufferUnderrunEvent&) { invalid(); },
171 [this](const BufferingThresholdReachedEvent&) {
174 },
175 [this](const ScheduleResyncAtPtsMsEvent& s) {
178 },
179 [&invalid](const ResyncEvent&) { invalid(); },
180 [this](const DisplayChangeEvent& d) {
182 },
183 }, event);
184 break;
185 case State::PLAY:
186 std::visit(overload{
187 [this](const PlayEvent&) {
189 },
190 [this](const PauseEvent&) {
192 },
193 [this](const StopEvent&) {
195 },
196 [this](const TrickSpeedEvent& t) {
197 m_pDevice->SetTrickSpeed(t.speed, t.active, t.forward);
199 },
200 [this](const StillPictureEvent& s) {
201 m_pDevice->HandleStillPicture(s.data, s.size);
202 },
203 [this, &needsResume](const DetachEvent&) {
205 needsResume = false;
206 },
207 [&invalid](const AttachEvent&) { invalid(); },
208 [this](const BufferUnderrunEvent&) {
210 },
211 [&invalid](const BufferingThresholdReachedEvent&) { /* ignore */ },
212 [this](const ScheduleResyncAtPtsMsEvent& s) {
214 },
215 [this](const ResyncEvent&) {
217 },
218 [this](const DisplayChangeEvent& d) {
220 },
221 }, event);
222 break;
224 std::visit(overload{
225 [this](const PlayEvent&) {
227 },
228 [this](const PauseEvent&) {
229 m_pDevice->PausePlayback(false);
230 },
231 [this](const StopEvent&) {
233 },
234 [this](const TrickSpeedEvent& t) {
235 // resume from pause, or change trick speed direction/speed
236 m_pDevice->SetTrickSpeed(t.speed, t.active, t.forward);
238 },
239 [this](const StillPictureEvent& s) {
240 m_pDevice->HandleStillPicture(s.data, s.size);
241 },
242 [this, &needsResume](const DetachEvent&) {
244 needsResume = false;
245 },
246 [&invalid](const AttachEvent&) { invalid(); },
247 [this](const BufferUnderrunEvent&) { /* ignore during trick speed. Fast forward/reverse as fast and as demanded as possible */ },
250 [&invalid](const ResyncEvent&) { invalid(); },
251 [this](const DisplayChangeEvent& d) {
253 },
254 }, event);
255 break;
256 }
257
258 if (needsResume)
260
261 } // end of locked state machine context
262#ifdef USE_GLES
263 if (needsOglResume)
265#endif
266
267 uint64_t stopStateChange = cTimeMs::Now();
268 LOGDEBUG("STATE MACHINE: state change done in %d ms", (int)(stopStateChange - startStateChange));
269}
270
277 : cThread("event handler"),
278 m_pStateMachine(statemachine)
279{
280 Start();
281}
282
287{
288 Cancel(2);
289}
290
297{
298 std::lock_guard<std::mutex> lock(m_mutex);
299 m_eventQueue.push_back(event);
300}
301
306{
307 LOGDEBUG("device: event queue handler thread started");
308
309 while (Running()) {
310 std::vector<Event> local;
311 {
312 std::lock_guard<std::mutex> lock(m_mutex);
313 local.swap(m_eventQueue);
314 }
315
316 for (auto &event : local)
318
319 usleep(10000);
320 }
321
322 LOGDEBUG("device: event queue handler thread stopped");
323}
void AddEvent(Event)
Add an event to the queue.
std::vector< Event > m_eventQueue
event fifo queue
std::mutex m_mutex
queue mutex
~cEventHandler(void)
Stop and delete the event handler thread.
void Action(void)
Periodically send events in the queue to the final event receiver/handler.
cEventHandler(cStateMachine *)
Create and start the event handler thread.
cStateMachine * m_pStateMachine
pointer to state machine
Output Device Implementation.
void ResumeFromPause(void)
Resume playback from pause state.
bool HaltOpenGlThread(void)
Pause the OpenGL worker thread.
void HandleDisplayModeChange(const sDrmMode &)
Set the display mode.
bool IsDetachForced(void)
Returns true, the a detached plugin start was forced.
void SetTrickSpeed(double, bool, bool)
Set the trickspeed in the renderer.
void HaltVideoThreads(void)
Pause the rendering and decoder thread.
void ResumeVideoThreads(void)
Resume the rendering and decoder thread.
bool SchedulePlaybackStart(void)
Schedules the playback start.
void ResumeOpenGlThread(void)
Resume the OpenGL worker thread.
void EnterState(State)
Actions to be performed when entering a state.
int InitAudio(bool)
Init the audio lazily.
void LeaveState(State)
Actions to be performed when leaving a state.
void HandleStillPicture(const uchar *data, int size)
The still picture data received from VDR can contain multiple PES packets.
void ResetVideoFilter(void)
Reset the video deinterlace filter.
void PausePlayback(bool)
Pause the playback.
void SetDetachForced(void)
void ResumePlayback(void)
Resume playback again.
void ScheduleResyncAtPtsMs(int64_t)
Schedule aa a/v resync in the render thread at the given pts.
State Machine Implementation.
std::atomic< State > m_state
current state
cStateMachine(cSoftHdDevice *)
Create the state machine.
std::mutex m_mutex
state machine mutex
cSoftHdDevice * m_pDevice
pointer to the device
void ChangeState(State)
Sets the new state and triggers actions, which are necessary when leaving the old and entering the ne...
void OnEventReceived(const Event &)
Event handler for playback state transitions.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent, DisplayChangeEvent > Event
const char * EventToString(const Event &e)
State
const char * StateToString(State s)
@ PLAY
@ STOP
@ BUFFERING
@ TRICK_SPEED
@ DETACHED
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:45
#define LOGERROR
log to LOG_ERR
Definition logger.h:39
#define LOGWARNING
log to LOG_WARN
Definition logger.h:41
Logger Header File.
Output Device Header File.
Device State Machine and Event Handler Header File.