vdr-plugin-softhddevice-drm-gles 1.7.3-98a2ef7
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::OsdDraw(), 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::OsdDraw()
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->SetTrickSpeed(0, false, false);
230 m_pDevice->PausePlayback(false);
231 },
232 [this](const StopEvent&) {
234 },
235 [this](const TrickSpeedEvent& t) {
236 // resume from pause, or change trick speed direction/speed
237 m_pDevice->SetTrickSpeed(t.speed, t.active, t.forward);
239 },
240 [this](const StillPictureEvent& s) {
241 m_pDevice->HandleStillPicture(s.data, s.size);
242 },
243 [this, &needsResume](const DetachEvent&) {
245 needsResume = false;
246 },
247 [&invalid](const AttachEvent&) { invalid(); },
248 [this](const BufferUnderrunEvent&) { /* ignore during trick speed. Fast forward/reverse as fast and as demanded as possible */ },
251 [&invalid](const ResyncEvent&) { invalid(); },
252 [this](const DisplayChangeEvent& d) {
254 },
255 }, event);
256 break;
257 }
258
259 if (needsResume)
261
262 } // end of locked state machine context
263#ifdef USE_GLES
264 if (needsOglResume)
266#endif
267
268 uint64_t stopStateChange = cTimeMs::Now();
269 LOGDEBUG("STATE MACHINE: state change done in %d ms", (int)(stopStateChange - startStateChange));
270}
271
278 : cThread("event handler"),
279 m_pStateMachine(statemachine)
280{
281 Start();
282}
283
288{
289 Cancel(2);
290}
291
298{
299 std::lock_guard<std::mutex> lock(m_mutex);
300 m_eventQueue.push_back(event);
301}
302
307{
308 LOGDEBUG("device: event queue handler thread started");
309
310 while (Running()) {
311 std::vector<Event> local;
312 {
313 std::lock_guard<std::mutex> lock(m_mutex);
314 local.swap(m_eventQueue);
315 }
316
317 for (auto &event : local)
319
320 usleep(10000);
321 }
322
323 LOGDEBUG("device: event queue handler thread stopped");
324}
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:46
#define LOGERROR
log to LOG_ERR
Definition logger.h:40
#define LOGWARNING
log to LOG_WARN
Definition logger.h:42
Logger Header File.
Output Device Header File.
Device State Machine and Event Handler Header File.