vdr-plugin-drmdevice 2.0.0-46418f1
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 "device.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 // cOutputDevice::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 cOutputDevice::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.
Definition device.h:72
void HandleStillPicture(const uchar *data, int size)
The still picture data received from VDR can contain multiple PES packets.
Definition device.cpp:1615
void HandleDisplayModeChange(const sDrmMode &)
Set the display mode.
Definition device.cpp:1647
void EnterState(State)
Actions to be performed when entering a state.
Definition device.cpp:1804
void LeaveState(State)
Actions to be performed when leaving a state.
Definition device.cpp:1737
void ResumeVideoThreads(void)
Resume the rendering and decoder thread.
Definition device.cpp:1911
bool HaltOpenGlThread(void)
Pause the OpenGL worker thread.
Definition device.cpp:1921
void ResetVideoFilter(void)
Reset the video deinterlace filter.
Definition device.cpp:1966
void ResumePlayback(void)
Resume playback again.
Definition device.cpp:2094
void SetDetachForced(void)
Definition device.h:227
void HaltVideoThreads(void)
Pause the rendering and decoder thread.
Definition device.cpp:1902
bool IsDetachForced(void)
Returns true, the a detached plugin start was forced.
Definition device.cpp:1939
void SetTrickSpeed(double, bool, bool)
Set the trickspeed in the renderer.
Definition device.cpp:1979
int InitAudio(bool)
Init the audio lazily.
Definition device.cpp:1952
void ResumeOpenGlThread(void)
Resume the OpenGL worker thread.
Definition device.cpp:1929
void PausePlayback(bool)
Pause the playback.
Definition device.cpp:2080
void ScheduleResyncAtPtsMs(int64_t)
Schedule aa a/v resync in the render thread at the given pts.
Definition device.cpp:2034
bool SchedulePlaybackStart(void)
Schedules the playback start.
Definition device.cpp:1994
void ResumeFromPause(void)
Resume playback from pause state.
Definition device.cpp:2044
State Machine Implementation.
cStateMachine(cOutputDevice *)
Create the state machine.
cOutputDevice * m_pDevice
pointer to the device
std::atomic< State > m_state
current state
std::mutex m_mutex
state machine mutex
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.
Output Device Header File.
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.
Device State Machine and Event Handler Header File.