vdr-plugin-softhddevice-drm-gles 1.7.3-98a2ef7
softhddevice.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
18#include <chrono>
19#include <mutex>
20#include <variant>
21#include <libintl.h>
22
23extern "C" {
24#include <libavcodec/avcodec.h>
25}
26
27#include <vdr/dvbspu.h>
28#include <vdr/skins.h>
29#include <vdr/status.h>
30#include <vdr/thread.h>
31
32#include "audio.h"
33#include "codec_audio.h"
34#include "config.h"
35#include "grab.h"
36#include "hardwaredevice.h"
37#include "jittertracker.h"
38#include "logger.h"
39#include "pes.h"
40#include "pipreceiver.h"
41#include "softhddevice.h"
42#include "softhdosdprovider.h"
43#include "statemachine.h"
44#include "videorender.h"
45#include "videostream.h"
46
55 : m_pConfig(config)
56{
57 m_pStateMachine = std::make_unique<cStateMachine>(this);
58}
59
66{
67 if (!m_initialized)
68 return;
69
70 m_initialized = false; // not necessary, just for documentation
71
72 delete m_pEventHandler;
73 delete m_pHardwareDevice;
74 delete m_pSpuDecoder;
75}
76
77/*********************************************************************
78 * VDR cPlugin interface (wrapped by cPluginSoftHdDevice)
79 ********************************************************************/
80
85{
86 LOGDEBUG("device: %s:", __FUNCTION__);
87
88 // the following are deleted in the destructor
92
93 auto channelSwitchStartTime = std::chrono::steady_clock::now();
94 LOGGER->SetChannelSwitchStartTime(channelSwitchStartTime);
95 LOGGER->SetChannelSwitchDataReceivedTime(channelSwitchStartTime);
96
99
100 m_initialized = true;
101
102 return true;
103}
104
109{
110 LOGDEBUG("device: %s", __FUNCTION__);
112
113 return true;
114}
115
120{
121 LOGDEBUG("device: %s", __FUNCTION__);
124}
125
126/*********************************************************************
127 * VDR cDevice interface
128 ********************************************************************/
129
136{
137 LOGDEBUG("device: %s: %d", __FUNCTION__, on);
138
139 if (on)
140 m_pOsdProvider = new cSoftOsdProvider(this); // no need to delete it, VDR does it
141
142 cDevice::MakePrimaryDevice(on);
143}
144
149{
150 bool hasDecoder = !IsDetached();
151
152// LOGDEBUG("device: %s: %d", __FUNCTION__, hasDecoder);
153
154 return hasDecoder;
155}
156
164{
165 LOGDEBUG("device: %s:", __FUNCTION__);
166 if (!IsPrimaryDevice())
167 return NULL;
168
169 return m_pSpuDecoder;
170}
171
181uchar *cSoftHdDevice::GrabImage(int &size, bool jpeg, int quality, int width, int height)
182{
183 if (!width || !height) {
184 LOGWARNING("device: %s: width or height is 0 - skip!", __FUNCTION__);
185 return nullptr;
186 }
187
188 if (IsDetached())
189 return nullptr;
190
191 if (m_pGrab->IsActive()) {
192 LOGWARNING("device: %s: wait for the last grab to be finished - skip!", __FUNCTION__);
193 return nullptr;
194 }
195
196 LOGDEBUG2(L_GRAB, "device: %s: %d, %d, %d, %dx%d", __FUNCTION__, size, jpeg, quality, width, height);
197
198 if (!m_pGrab->Start(jpeg, quality, width, height, m_screenWidth, m_screenHeight))
199 return nullptr;
200
201 if (!m_pGrab->ProcessGrab())
202 return nullptr;
203
204 size = m_pGrab->Size();
206
207 m_pGrab->Finish();
208
209 return result;
210}
211
219{
220 LOGDEBUG("device: %s: %d", __FUNCTION__, videoDisplayFormat);
221
222 cDevice::SetVideoDisplayFormat(videoDisplayFormat);
223}
224
236{
237 LOGDEBUG("device: %s: %d", __FUNCTION__, videoFormat16_9);
238
239 // FIXME: 4:3 / 16:9 video format not supported.
240 SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
241}
242
255void cSoftHdDevice::GetVideoSize(int &width, int &height, double &aspectRatio)
256{
257// LOGDEBUG("device: %s: %d x %d @ %f", __FUNCTION__, *width, *height, *aspectRatio);
258
259 if (IsDetached()) { // return default values according to vdr docs
260 width = 0;
261 height = 0;
262 aspectRatio = 1.0;
263 return;
264 }
265
266 m_pVideoStream->GetVideoSize(&width, &height, &aspectRatio);
267}
268
278void cSoftHdDevice::GetOsdSize(int &width, int &height, double &aspectRatio)
279{
280 if (IsDetached()) { // hardcode to 1920x1080 in detached state
281 width = 1920;
282 height = 1080;
283 aspectRatio = (double)width / (double)height;
284 return;
285 }
286
287 std::lock_guard<std::mutex> lock(m_sizeMutex);
288 width = m_osdWidth;
289 height = m_osdHeight;
290 aspectRatio = (double)width / (double)height;
291}
292
299{
300 if (IsDetached())
301 return;
302
303 LOGDEBUG("device: %s: %d", __FUNCTION__, volume);
305 m_pAudio->SetVolume((volume * 1000) / 255);
306}
307
312{
313 bool canReplay = !IsDetached();
314
315 LOGDEBUG("device: %s: %d", __FUNCTION__, canReplay);
316
317 return canReplay;
318}
319
326{
327 LOGDEBUG("device: %s: %d", __FUNCTION__, play_mode);
328
329 // A new play mode arrived, attach first if we did detach because of an external player
333 }
334
335 auto now = std::chrono::steady_clock::now();
336 auto durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
337
338 SetDrain(false);
339
340 switch (play_mode) {
341 case pmNone:
343 break;
344 case pmAudioVideo:
345 case pmAudioOnly:
346 case pmAudioOnlyBlack:
347 case pmVideoOnly:
348 m_logPlaybackStart = true;
349 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms SetPlayMode()", durationSinceChannelSwitchMs);
351 break;
353 // External players like mpv (vdr-plugin-mpv) want to acquire DRM/ALSA
354 // so we release it here and set a flag. As soon as the next SetPlayMode arrives
355 // we then can attach again before changing to the new playmode.
359 break;
360 default:
361 LOGERROR("device: %s: playmode not supported %d", play_mode);
362 return false;
363 break;
364 }
365
366 return true;
367}
368
382int cSoftHdDevice::PlayAudio(const uchar *data, int size, uchar id)
383{
384// LOGDEBUG("device: %s: %p %p %d %d", __FUNCTION__, this, data, size, id);
385 if (IsDetached())
386 return size;
387
388 m_receivedAudio = true;
389
390 // Special audio-only trickspeed handling:
391 // Advance the pts and block the thread depending on the pts difference and trickspeed factor.
392 // Don't forward any data to the hardware, because audio is muted anyway.
395 cPesAudio pesPacket((const uint8_t*)data, size);
396 if (pesPacket.IsValid() && pesPacket.GetPts() != AV_NOPTS_VALUE)
398
399 return size;
400 }
401
402 if (m_pAudio->IsBufferFull())
403 return 0;
404
405 cPesAudio pesPacket((const uint8_t*)data, size);
406
407 if (!pesPacket.IsValid()) {
409
410 return size;
411 }
412
414 auto now = std::chrono::steady_clock::now();
415 auto durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
416 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms PlayAudio() first valid audio data", durationSinceChannelSwitchMs);
417
419 LOGGER->SetChannelSwitchDataReceivedTime(now);
420
422 }
423
424 if (Transferring()) { // compensation is only necessary with live streams
429 }
430
431 if (m_audioChannelID != id) {
435 LOGDEBUG("device: %s: new channel id 0x%02X", __FUNCTION__, m_audioChannelID);
436 }
437
438 m_audioReassemblyBuffer.Push(pesPacket.GetPayload(), pesPacket.GetPayloadSize(), pesPacket.GetPts());
439
440 // fire playback start, if all buffers reached the threshold fill levels
443
444 // unpause audio, if the fast channel switch mode wants it to be started,
445 // the audio buffer reached the threshold and we are in live tv mode
447 m_pAudio->SetPaused(false);
448
450 do {
452 break;
453
455 // The playback has just started
458 }
459
463 } while (avpkt != nullptr);
464
465 return size;
466}
467
482int cSoftHdDevice::PlayVideo(const uchar *data, int size)
483{
484// LOGDEBUG("device: %s: %p %d", __FUNCTION__, data, size);
485 if (IsDetached())
486 return size;
487
488 m_receivedVideo = true;
489
491}
492
498{
499 if (IsDetached())
500 return AV_NOPTS_VALUE;
501
502 switch (m_playbackMode) {
503 case NONE:
504 return AV_NOPTS_VALUE;
505 case AUDIO_AND_VIDEO:
506 case VIDEO_ONLY:
507 return m_pRender->GetVideoClock();
508 case AUDIO_ONLY:
510 if (m_pStateMachine->GetState() == State::TRICK_SPEED && !slowForward)
512
514 }
515
516 abort();
517}
518
527{
529 return rect;
530
531 double scaleFactor = std::min((double)m_screenWidth / m_osdWidth, (double)m_screenHeight / m_osdHeight);
532 int width = std::lround(scaleFactor * rect.Width());
533 int height = std::lround(scaleFactor * rect.Height());
534 int x = std::lround(scaleFactor * rect.X());
535 int y = std::lround(scaleFactor * rect.Y());
536
537 x = std::max(0, x);
538 y = std::max(0, y);
539 if (x + width > m_screenWidth)
540 width = m_screenWidth - x;
541 if (y + height > m_screenHeight)
542 height = m_screenHeight - y;
543
544 if (width <= 0 || height <= 0)
545 return cRect::Null;
546
547 LOGDEBUG2(L_DRM, "device: %s: scale rect %dx%d-%d|%d -> %dx%d-%d|%d", __FUNCTION__,
548 rect.Width(), rect.Height(), rect.X(), rect.Y(), width, height, x, y);
549
550 return cRect(x, y, width, height);
551}
552
562{
563 if (IsDetached())
564 return;
565
566 LOGDEBUG2(L_OSD, "device: %s: %dx%d%+d%+d",
567 __FUNCTION__, rect.Width(), rect.Height(), rect.X(), rect.Y());
568
569 if (m_pRender)
571}
572
585void cSoftHdDevice::TrickSpeed(int speed, bool forward)
586{
587 LOGDEBUG("device: %s: %d %s", __FUNCTION__, speed, forward ? "forward" : "backward");
588
589 // This normalizes the VDR frame displaying count into a factor, representing how fast/slow the playback shall be.
590 // For example, a factor of 2.0 means twice as fast as normal, a factor of 0.5 means half as fast as normal (slow-mo).
591 // This is necessary because VDR sends only I-frames during trickspeed, but the distance between I-frames depends on the encoding parameters.
592 // Therefore, we send a normalized factor for the further components, which then calculate the necessary frame displaying count by considering the distance between I-frames.
593
594 double normalizedSpeed = 1;
595 static constexpr double MAX_SPEED = 3;
596
597 // these are arbitrary values, which feel just right
598 static constexpr double FAST_TRICKSPEED_FACTOR = 5; // the higher the factor, the faster the fast forward/reverse
599 static constexpr double SLOW_FORWARD_FACTOR = 2; // the higher the factor, the slower the slow-mo
600
601 // Fastest speed in reverse slow-mo is the original speed. Slower speeds are too slow, because of the already low frame rate.
602 static constexpr double SLOW_REVERSE_FACTOR = 1;
603
604 // speed of the trickspeed (VDR's magic frame displaying count)
605 switch (speed) {
606 case 6:
607 case 8:
608 case 63:
609 normalizedSpeed = 1; // slowest (both, in fast trickspeed and slow-mo)
610 break;
611 case 3:
612 case 4:
613 case 48:
614 normalizedSpeed = 2;
615 break;
616 case 1:
617 case 2:
618 case 24:
619 normalizedSpeed = 3; // fastest (both, in fast trickspeed and slow-mo)
620 break;
621 }
622
623 // figure out if VDR demands slow-mo or fast trickspeed
624 double tmp;
625 switch (speed) {
626 case 8:
627 case 4:
628 case 2:
629 case 63:
630 case 48:
631 case 24:
632 // slow-mo
633 tmp = (MAX_SPEED + 1) - normalizedSpeed;
634
635 if (forward)
637 else
639
640 normalizedSpeed = 1 / tmp;
641 break;
642 default:
643 // fast trickspeed
645 break;
646 }
647
648 TriggerEvent(TrickSpeedEvent{normalizedSpeed, speed != 0, forward});
649}
650
659{
660 LOGDEBUG("device: %s:", __FUNCTION__);
661 cDevice::Clear();
662
663 if (IsDetached())
664 return;
665
666 m_pRender->Halt();
668
669 SetDrain(false);
670
673
677
680
681 m_pRender->Reset();
682
683 m_pAudio->SetPaused(true);
685 FlushAudio();
686
687 m_pStateMachine->ChangeState(BUFFERING);
688
689 m_pRender->Resume();
691}
692
699{
700 LOGDEBUG("device: %s:", __FUNCTION__);
701 cDevice::Play();
702
704}
705
710{
711 LOGDEBUG("device: %s:", __FUNCTION__);
712 cDevice::Freeze();
713
715}
716
723void cSoftHdDevice::StillPicture(const uchar *data, int size)
724{
725 LOGDEBUG("device: %s: %s %p %d", __FUNCTION__, data[0] == 0x47 ? "ts" : "pes", data, size);
726
727 if (data[0] == 0x47) { // ts sync byte
728 cDevice::StillPicture(data, size);
729 return;
730 }
731
732 TriggerEvent(StillPictureEvent{data, size});
733}
734
748{
749// LOGDEBUG("device: %s: timeout %d", __FUNCTION__, timeout_ms);
750 if (IsDetached())
751 return true;
752
754 return true;
755
756 usleep(timeoutMs * 1000);
757
758 return false;
759}
760
777{
778 if (IsDetached())
779 return true;
780
781// LOGDEBUG("device: %s: timeout %d ms", __FUNCTION__, timeoutMs);
782
783 const auto buffersEmpty = [&]() {
784 return m_playbackMode == AUDIO_ONLY
787 };
788
790 while (!buffersEmpty() && !timeout.TimedOut())
791 cCondWait::SleepMs(std::min(5, timeoutMs));
792
793 return buffersEmpty();
794}
795
796#if APIVERSNUM >= 30014
802bool cSoftHdDevice::DrainDevice(void)
803{
804 if (IsDetached())
805 return true;
806
807 // enter drain mode once
808 if (!m_draining) {
811
812 if (!m_pVideoStream->Drain())
813 return false;
814
815 SetDrain(true);
816 }
817
818 const auto buffersEmpty = [&]() {
819 return m_playbackMode == AUDIO_ONLY
822 };
823
824 if (!buffersEmpty())
825 return false;
826
827 LOGDEBUG("device: %s: drained, buffers are empty", __FUNCTION__);
828 SetDrain(false);
829
830 return true;
831}
832#endif
833
834/*********************************************************************
835 * VDR cStatus interface
836 ********************************************************************/
837
844void cSoftHdDevice::ChannelSwitch(const cDevice *device, int channelNum, bool liveView)
845{
846 if (device != cDevice::PrimaryDevice() || !liveView || channelNum != 0)
847 return;
848
849 auto now = std::chrono::steady_clock::now();
850 LOGGER->SetChannelSwitchStartTime(now);
851 LOGDEBUG2(L_AV_SYNC, "TRACE: channel switch");
852}
853
860 [[maybe_unused]]const char *name,
861 [[maybe_unused]]const char *filenameDevice,
862 bool on)
863{
864 if (!on)
865 return;
866
867 auto now = std::chrono::steady_clock::now();
868 LOGGER->SetChannelSwitchStartTime(now);
869 LOGDEBUG2(L_AV_SYNC, "TRACE: replay start");
870}
871
872/*********************************************************************
873 * cSoftHdDevice public API - playback, display, decoder control
874 ********************************************************************/
875
884
885
900
908
915{
917
921 if (!mode->width || !m_pRender->CanHandleMode(mode))
923 } else if (idx >= CONFIG_DISPLAY_MODE_MANUAL)
925
926 // Check, if the requested mode differs from the current one at all
927 if (!m_pConfig->CompareCurrentMode(mode)) {
928 LOGDEBUG("Add display mode change event to %s mode %dx%d@%.2f%s",
929 idx == CONFIG_DISPLAY_MODE_DEFAULT ? "default" :
930 (idx == CONFIG_DISPLAY_MODE_FOLLOW_VIDEO ? "follow video" :
931 (idx == CONFIG_DISPLAY_MODE_FOLLOW_VIDEO_INTERLACED ? "follow video interlaced" :
932 "fixed")),
933 mode->width, mode->height, mode->refreshRateHz, mode->interlaced ? "i" : "");
935 }
936}
937
966{
967 if (m_pStateMachine->GetState() == PLAY && m_playbackMode == AUDIO_ONLY && m_receivedVideo) {
969 return false;
970 }
971
972 if (m_pStateMachine->GetState() != BUFFERING)
973 return false;
974
978
979 // Assume audio only or video only if no PES fragment from the other stream has been received, while the buffering threshold of the other stream is reached.
980 // Check for buffer fill level only if at least one PES packet was reassembled and pushed to the respective decoder.
983
985
987 LOGDEBUG("device: %s: Detected audio only", __FUNCTION__);
988 return true;
989 }
990
992 LOGDEBUG("device: %s: Detected video only", __FUNCTION__);
993 return true;
994 }
995
996 // In audio/video mode we need valid PTS values for both streams and a video frame must have reached the output buffer.
998 return false;
999
1003
1006
1009
1010 // used for diagnostics:
1011 // both audio and video have reached their buffering thresholds, but playback may
1012 // still be waiting for audio/video sync
1014
1015 // in CHANNEL_SWITCH_FAST_AUDIO mode audio was started already,
1016 // do not start video until audio has caught up with the video pts
1019
1021 auto now = std::chrono::steady_clock::now();
1022 auto durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
1023 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms firing BufferingThresholdReached - PTS: %s (audio), %s (video) - buffer fill levels: %ldms (audio) %ldms (video)",
1029
1030 return true;
1031 }
1032
1034 auto now = std::chrono::steady_clock::now();
1035 auto durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
1036 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms BufferingThresholdReached, but video waits for audio (-%dms) - PTS: %s (audio), %s (video) - buffer fill levels: %ldms (audio) %ldms (video)",
1043 m_logPlaybackStart = false;
1044 }
1045
1046 return false;
1047}
1048
1064{
1065 if (m_pStateMachine->GetState() != BUFFERING)
1066 return false;
1067
1069 return false;
1070
1071 if (!m_pAudio->IsPaused())
1072 return false;
1073
1077
1078 // only start audio playback if the device also received and presented video already
1079 // -> video is always first
1081 return false;
1082
1084
1085 // the needed audio threshold fill level wasn't reached yet
1087 return false;
1088
1089 // drop audio data here, if the buffer fill level exceeds the threshold
1090 // - otherwise a later playback start is unneccessarily delayed when all buffers
1091 // reached their threshold but the rest of the audio data has still to be played out ...
1094
1095 // get new buffer filllevel for correct log message, because frames have been dropped
1097
1098 auto now = std::chrono::steady_clock::now();
1099 auto durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
1100 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms firing AudioBufferingThresholdReached, threshold %dms - PTS: %s (audio), %s (video) - buffer fill levels: %ldms (audio)",
1106
1107 return true;
1108}
1109
1110/*********************************************************************
1111 * cSoftHdDevice public API - osd control
1112 ********************************************************************/
1113
1114#ifdef USE_GLES
1122
1130
1140
1150#endif
1151
1159{
1160 if (IsDetached())
1161 return;
1162
1164}
1165
1178void cSoftHdDevice::OsdDraw(int xi, int yi, int height, int width, int pitch,
1179 const uint8_t * argb, int x, int y)
1180{
1181 if (IsDetached())
1182 return;
1183
1184 m_pRender->OsdDraw(xi, yi, height, width, pitch, argb, x, y);
1185}
1186
1193void cSoftHdDevice::SetOsdSize(int width, int height)
1194{
1195 std::lock_guard<std::mutex> lock(m_sizeMutex);
1196 m_osdWidth = width;
1197 m_osdHeight = height;
1198}
1199
1206void cSoftHdDevice::SetScreenSize(int width, int height)
1207{
1208 std::lock_guard<std::mutex> lock(m_sizeMutex);
1209 m_screenWidth = width;
1210 m_screenHeight = height;
1211}
1212
1213/*********************************************************************
1214 * cSoftHdDevice public API - audio control
1215 ********************************************************************/
1216
1226
1231{
1232 LOGDEBUG("%s:", __FUNCTION__);
1233 m_audioChannelID = -1;
1234}
1235
1236/*********************************************************************
1237 * cSoftHdDevice public API - logging, statistics
1238 ********************************************************************/
1239
1248{
1249 *duped = 0;
1250 *dropped = 0;
1251 *counter = 0;
1252 if (m_pRender) {
1254 }
1255}
1256
1257/*********************************************************************
1258 * cSoftHdDevice public API - mediaplayer
1259 ********************************************************************/
1260
1272
1284
1294{
1295 if (m_pAudio->LazyInit())
1296 LOGFATAL("%s: audio init failed, abort!", __FUNCTION__);
1297
1298 if (m_pAudio->IsBufferFull())
1299 return 0;
1300
1302 return 1;
1303}
1304
1314{
1315 if (m_pAudio->LazyInit())
1316 LOGFATAL("%s: audio init failed, abort!", __FUNCTION__);
1317
1319 return 0;
1320
1322
1323 return 1;
1324}
1325
1326/*********************************************************************
1327 * cSoftHdDevice public API - detach/ attach
1328 ********************************************************************/
1329
1337{
1338 if (cDevice::Replaying()) {
1339 LOGDEBUG("device: %s: Device is replaying, stop replay first", __FUNCTION__);
1340 StopReplay();
1341 }
1342
1343 if (IsPrimaryDevice(false)) {
1344 m_needsMakePrimary = true;
1345 MakePrimaryDevice(false);
1346 }
1347
1350}
1351
1359{
1360 m_forceDetached = false;
1361
1362 if (m_needsMakePrimary) {
1363 MakePrimaryDevice(true);
1364 m_needsMakePrimary = false;
1365 }
1366
1368}
1369
1374{
1375 auto lock = m_pStateMachine->Lock();
1376
1377 return m_pStateMachine->GetState() == State::DETACHED;
1378}
1379
1380/*********************************************************************
1381 * cSoftHdDevice public API - pip
1382 ********************************************************************/
1383
1394int cSoftHdDevice::PlayPipVideo(const uchar *data, int size)
1395{
1396// LOGDEBUG("device: %s: %p %d", __FUNCTION__, data, size);
1397
1398 // This is a bit hacky:
1399 // Because we do no sync with the pip stream, we simply drop data
1400 // if the input buffer is full -> this prevents us from having a buffer overflow
1401 // caused by the pip stream.
1403 return size;
1404
1405 return PlayVideoInternal(m_pPipStream, &m_pipReassemblyBuffer, data, size, false, false);
1406}
1407
1431
1436{
1437 auto lock = m_pStateMachine->Lock();
1438
1439 return m_pPipHandler->IsEnabled();
1440}
1441
1449
1450/*
1451 * Wrapper functions for cVideoRender and cPipHandler
1452 */
1454{
1455 auto lock = m_pStateMachine->Lock();
1456
1457 m_pRender->Halt();
1459 m_pRender->Resume();
1460};
1461
1463{
1464 auto lock = m_pStateMachine->Lock();
1465
1466 m_pRender->Halt();
1467 m_pRender->SetPipActive(active);
1468 m_pRender->Resume();
1469};
1470
1471/*********************************************************************
1472 * cSoftHdDevice private functions
1473 ********************************************************************/
1474
1483static void PrintStreamData(const uchar *payload)
1484{
1485 LOGDEBUG2(L_CODEC, "Stream: %02X%02X%02X | %02X | %02X%02X | %02X%02X%02X | %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
1486 payload[0],
1487 payload[1],
1488 payload[2],
1489 payload[3],
1490 payload[4],
1491 payload[5],
1492 payload[6],
1493 payload[7],
1494 payload[8],
1495 payload[9],
1496 payload[10],
1497 payload[11],
1498 payload[12],
1499 payload[13],
1500 payload[14],
1501 payload[15],
1502 payload[16],
1503 payload[17],
1504 payload[18],
1505 payload[19],
1506 payload[20],
1507 payload[21],
1508 payload[22],
1509 payload[23],
1510 payload[24]
1511 );
1512}
1513
1525{
1526 // LOGDEBUG("device: %s: %p %d %s", __FUNCTION__, data, size, mainStream ? "video" : "pip");
1527
1528 if (stream->IsInputBufferFull())
1529 return 0;
1530
1531 cPesVideo pesPacket((const uint8_t*)data, size);
1532
1533 if (!pesPacket.IsValid()) {
1534 buffer->Reset();
1535
1536 return size;
1537 }
1538
1539 if (mainStream) {
1540 if (!m_receivedValidVideo) {
1541 auto now = std::chrono::steady_clock::now();
1542 auto durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
1543 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms PlayVideo() first valid video data", durationSinceChannelSwitchMs);
1544
1546 LOGGER->SetChannelSwitchDataReceivedTime(now);
1547
1548 m_receivedValidVideo = true;
1549 }
1550 }
1551
1552 if (trackJitter) {
1556 }
1557
1558 if (stream->GetCodecId() == AV_CODEC_ID_NONE) {
1559 // The playback has just started
1560 if (!pesPacket.HasPts() || !buffer->ParseCodecHeader(pesPacket.GetPayload(), pesPacket.GetPayloadSize())) {
1561 // received the middle of fragmented data, wait for the next PES packets with the start of a new frame
1562 return size;
1563 }
1564
1565 auto now = std::chrono::steady_clock::now();
1566 auto durationSinceChannelSwitchMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - LOGGER->GetChannelSwitchStartTime()).count();
1567 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms PlayVideo() first decodeable video packet", durationSinceChannelSwitchMs);
1568
1569 PrintStreamData(data);
1570 buffer->Push(pesPacket.GetPayload(), pesPacket.GetPayloadSize(), pesPacket.GetPts());
1571
1572 stream->Open(buffer->GetCodec());
1573 } else {
1574 int payloadOffset = 0;
1575 if (pesPacket.HasPts() && !buffer->IsEmpty()) {
1576 // received the first fragment of a new frame, finish the current reassembly buffer into an AVPacket
1577 stream->PushAvPacket(buffer->PopAvPacket());
1578
1579 // populate the cleared buffer with the next frame
1580 if (buffer->HasLeadingZero(pesPacket.GetPayload(), pesPacket.GetPayloadSize()))
1581 payloadOffset = 1; // H.264/HEVC streams may have a leading zero byte before the start code
1582 }
1583
1584 buffer->Push(pesPacket.GetPayload() + payloadOffset, pesPacket.GetPayloadSize() - payloadOffset, pesPacket.GetPts());
1585 }
1586
1587 return size;
1588}
1589
1600
1608void cSoftHdDevice::HandleStillPicture(const uchar *data, int size)
1609{
1612
1613 // skip BufferUnderrunEvent{VIDEO} in renderer
1615
1616 const uchar *currentPacketStart = data;
1617 while (currentPacketStart < data + size) {
1619
1620 if (pesPacket.IsValid())
1622 else {
1623 LOGWARNING("device: %s: invalid PES packet", __FUNCTION__);
1624 break;
1625 }
1626
1627 currentPacketStart += pesPacket.GetPacketLength();
1628 }
1629
1631 m_pVideoStream->ResetInputPts(); // stillpicture shouldn't trigger having video data
1633}
1634
1641{
1642 LOGDEBUG("Set display mode: %dx%d@%.2f%s",
1643 mode.width,
1644 mode.height,
1645 mode.refreshRateHz,
1646 mode.interlaced ? "i" : "");
1647
1650}
1651
1673
1686
1694
1702{
1703 LOGDEBUG("device: %s: %s draining", __FUNCTION__, on ? "start" : "stop");
1704 m_draining = on;
1705}
1706
1707/*********************************************************************
1708 * cSoftHdDevice state transitioning functions
1709 ********************************************************************/
1710
1718{
1719 m_pStateMachine->OnEventReceived(event);
1720}
1721
1731{
1732 switch (state) {
1733 case PLAY:
1736 m_pAudio->SetPaused(true);
1738 break;
1739 case BUFFERING:
1742 break;
1743 case TRICK_SPEED:
1744 // The filter thread needs to be restarted for interlaced streams to be rendered with deinterlacer again. It is started lazily.
1746 m_pRender->SetTrickSpeed(0, false, false);
1747 m_pAudio->SetTrickSpeed(0, false, false);
1755 break;
1756 case STOP:
1757 m_receivedAudio = false;
1758 m_receivedVideo = false;
1759 m_receivedValidAudio = false;
1760 m_receivedValidVideo = false;
1761 break;
1762 case DETACHED:
1763 m_pAudio = new cSoftHdAudio(this);
1764 m_pRender = new cVideoRender(this);
1768 int err = m_pRender->Init(); // starts display thread
1769 if (err < 0) { // drm init failed -> force detached mode
1770 delete m_pAudioDecoder;
1771 delete m_pVideoStream;
1772 delete m_pGrab;
1773 delete m_pRender;
1774 delete m_pAudio;
1775 LOGERROR("device: display init failed -> start in detached mode");
1776 m_forceDetached = true;
1777 break;
1778 }
1779 m_pVideoStream->StartDecoder(); // starts decoding thread
1781 m_pPipStream->StartDecoder(); // starts decoding thread
1782 m_pPipHandler = new cPipHandler(this);
1783 // Audio is init lazily (includes starting thread)
1784
1785 break;
1786 }
1787}
1788
1798{
1799 switch (state) {
1800 case BUFFERING:
1805 }
1806 break;
1807 case PLAY:
1808 // revert the video playback start release which was done in BUFFERING if we are audio-only
1812 }
1813
1815 m_pAudio->SetPaused(false);
1816
1817 if (m_playbackMode != AUDIO_ONLY) {
1819 m_pRender->SetStillpicture(false);
1821 }
1822 break;
1823 case TRICK_SPEED:
1824 // The filter thread needs to be restarted for interlaced streams
1825 // to be rendered without deinterlacer in trick speed mode.
1826 // It is started lazily.
1832 break;
1833 case STOP:
1834 if (m_forceDetached) {
1835 m_pStateMachine->SetState(DETACHED);
1836 break;
1837 }
1838
1839 FlushAudio();
1840
1843 m_pRender->Reset();
1845
1854
1855 break;
1856 case DETACHED:
1857 delete m_pPipHandler;
1858 m_pPipHandler = nullptr;
1859
1860 // resume the previously stopped threads
1862 m_pRender->Resume();
1863
1864 // now do the detach
1865 m_pPipStream->Exit();
1866 delete m_pPipStream;
1867
1868#ifdef USE_GLES
1869 // The opengl thread was probably locked before cmdCopyBufferToOutputFb().
1870 // 1) set running to false
1871 // 2) continue the thread (which will skip the waiting cmd->Execute())
1872 // 3) do the real thread cancel and cleanup
1873 // We need to keep this order to prevent a deadlock!
1877#endif
1878 m_pRender->Exit(); // render must be stopped before videostream!
1880 m_pAudio->Exit(); // audio must be stopped after renderer!
1881
1882 delete m_pAudioDecoder; // includes a Close()
1883 delete m_pVideoStream;
1884 delete m_pGrab;
1885 delete m_pRender;
1886 delete m_pAudio;
1887
1888 break;
1889 }
1890}
1891
1896{
1897 m_pRender->Halt();
1899}
1900
1909
1910#ifdef USE_GLES
1918
1927#endif
1928
1933{
1934 return m_forceDetached;
1935}
1936
1946{
1947 if (m_pAudio->LazyInit())
1948 return -1;
1949
1950 if (setVolume)
1951 m_pAudio->SetVolume((m_volume * 1000) / 255);
1952
1953 return 0;
1954}
1955
1964
1972void cSoftHdDevice::SetTrickSpeed(double speed, bool active, bool forward)
1973{
1974 m_pRender->SetTrickSpeed(speed, active, forward);
1975 m_pAudio->SetTrickSpeed(speed, active, forward);
1976}
1977
1988{
1991
1994 // store the first PTSes beforehand, because dropping samples/frames will change the output of GetFirst*PtsMsToPlay()
1997
1998 // don't drop old audio, if we want to wait for it in fast channel switch mode including audio playback
2001
2003 } else if (receivedAudio) {
2004 LOGDEBUG("device: audio only detected");
2007 } else if (receivedVideo) {
2008 LOGDEBUG("device: video only detected");
2011 } else {
2012 // Sometimes a DeviceClear() can jump in between signalling the ThresholdReachedEvent
2013 // and progressing it, e.g. the video thread signals the ThresholdReached, VDR sends a DeviceClear()
2014 // and OnEventReceived wants to process the ThresholdReachedEvent with empty buffers.
2015 LOGDEBUG("device: buffering threshold reached and no a/v available, keep BUFFERING state");
2016 return false;
2017 }
2018
2019 return true;
2020}
2021
2031
2038{
2039 // resume from pause
2041
2042 switch (m_playbackMode) {
2043 case AUDIO_ONLY:
2045 m_pAudio->SetPaused(false);
2046 break;
2047 case AUDIO_AND_VIDEO:
2050 if (audioBehindVideoByMs > 0) {
2052 m_pAudio->SetPaused(false);
2053 } else
2055
2056 // fallthrough
2057 case VIDEO_ONLY:
2058 m_pRender->SetStillpicture(false);
2060 break;
2061 case NONE:
2062 LOGFATAL("STATE MACHINE: play event in PLAY state with NONE playback mode. This is a bug.");
2063 break;
2064 }
2065}
2066
2083
2088{
2090}
Audio Interface Header File.
Audio Decoder.
Definition codec_audio.h:61
void FlushBuffers(void)
Flush the audio decoder buffers.
void Decode(const AVPacket *)
Forward an audio packet either to the decoder or passthrough.
AVCodecID GetCodecId() const
Definition codec_audio.h:70
void Open(AVCodecID, AVCodecParameters *=nullptr, AVRational={ .num=1,.den=PTSTICKS })
Open and initiate the audio decoder.
void SetPassthroughMask(int)
Set audio pass-through mask.
void Close(void)
Close the audio decoder.
Event handler thread.
void AddEvent(Event)
Add an event to the queue.
Hardware device.
int GetQuirks(void)
Get Hardware Quirks.
int GetShortTermMaxJitterMs(void)
int GetLongTermMaxJitterMs(void)
void PacketReceived(void)
Called each time a packet is received.
void Reset(void)
Resets the jitter tracker.
Main Video Stream.
Audio PES Packet Parser.
Definition pes.h:79
Video PES Packet Parser.
Definition pes.h:64
PiP Stream Handler.
Definition pipreceiver.h:55
void ChannelChange(int)
Change the pip channel.
void SwapPosition(void)
Swap pip between normal and alternative position.
void Toggle(void)
Toggle picture-in-picture.
void Disable(void)
Stop picture-in-picture.
void ChannelSwap(bool)
Swap the pip channel with main live channel.
bool IsEnabled(void)
Definition pipreceiver.h:68
void Enable(void)
Start picture-in-picture.
void SetSize(void)
Set size and position for the pip window.
PiP Video Stream.
AVPacket * PopAvPacket(void) override
Pop an audio AVPacket from the reassembly buffer.
Definition pes.cpp:486
Video Stream Reassembly Buffer.
Definition pes.h:145
AVPacket * PopAvPacket(void) override
Definition pes.h:148
bool HasLeadingZero(const uint8_t *, int)
Check if video data has a leading zero byte before the start code.
Definition pes.cpp:469
bool ParseCodecHeader(const uint8_t *, int)
Parse video codec header to detect codec type.
Definition pes.cpp:432
AVCodecID GetCodec(void)
Definition pes.h:128
void Reset(void)
Reset the reassembly buffer.
Definition pes.cpp:659
virtual void Push(const uint8_t *data, int size, int64_t pts)
Definition pes.h:123
bool IsEmpty(void)
Definition pes.h:125
Audio Interface.
Definition audio.h:51
void ResetHwDelayBaseline(void)
Reset the hw delay baseline.
Definition audio.cpp:1244
int64_t GetInputPtsMs(void)
Definition audio.h:72
void SetHwDelayBaseline(void)
Set the hw delay baseline.
Definition audio.cpp:1226
void AdvanceTrickSpeedPts(int64_t)
Advance the pts in trickspeed mode and delay "playback".
Definition audio.cpp:820
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
Definition audio.cpp:909
void SetVolume(int)
Set mixer volume (0-1000)
Definition audio.cpp:923
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition audio.cpp:420
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
Definition audio.cpp:775
void ClockDriftCompensation(void)
Calculate clock drift compensation.
Definition audio.cpp:1274
void SetTrickSpeed(double, bool, bool)
Set the trickspeed.
Definition audio.cpp:789
void Exit(void)
Cleanup audio output module (alsa)
Definition audio.cpp:1035
bool IsSlowTrickSpeed(void)
Definition audio.h:81
void SetPaused(bool)
Set audio playback pause state.
Definition audio.cpp:945
bool IsPaused(void)
Definition audio.h:59
bool IsBufferEmpty(void)
Definition audio.h:63
void SetPassthroughMask(int mask)
Definition audio.h:91
int LazyInit(void)
Initialize audio output module (alsa)
Definition audio.cpp:1013
bool HasInputPts(void)
Definition audio.h:71
void ResetTrickSpeedPts(void)
Definition audio.h:79
bool IsForwardTrickSpeed(void)
Definition audio.h:80
bool IsBufferFull(void)
Definition audio.h:62
int64_t GetTrickSpeedPtsTimebaseUnits(void)
Definition audio.h:78
void FlushBuffers(void)
Flush audio buffers.
Definition audio.cpp:723
Plugin Configuration.
Definition config.h:65
sDrmMode RequestedDrmMode
is set to the requested mode which should be changed to
Definition config.h:152
int ConfigVideoAudioDelayMs
config audio delay
Definition config.h:85
std::atomic< int > StatMaxLongTermAudioJitterMs
logged max overall audio jitter since stream start
Definition config.h:144
int ConfigVideoChannelSwitchMode
channel switch mode (enum ChannelSwitchMode)
Definition config.h:77
int ConfigMaxSizeGPUImageCache
config max gpu image cache size
Definition config.h:121
std::atomic< int > StatMaxShortTermAudioJitterMs
logged max audio jitter of the last 1000 packets
Definition config.h:143
int ConfigDecoderFallbackToSwNumPkts
maximum number of packets sent before fallback to sw decoder
Definition config.h:117
std::vector< sDrmMode > CollectedDrmModes
collected available drm modes on the current connector
Definition config.h:147
bool ConfigDisableDeint
disable deinterlacer
Definition config.h:115
int ConfigDisableOglOsd
config disable ogl osd
Definition config.h:135
int ConfigAdditionalBufferLengthMs
config size ms of a/v buffer
Definition config.h:114
bool CompareCurrentMode(sDrmMode *)
Return true, if the given mode is equal to the current display mode.
Definition config.cpp:164
std::atomic< int > StatMaxShortTermVideoJitterMs
logged max video jitter of the last 1000 packets
Definition config.h:145
sDrmMode AutoDetectedDrmMode
auto detected mode on the first startup (maybe equal to UserSetMode)
Definition config.h:148
int ConfigPipUseAlt
Definition config.h:103
std::atomic< int > StatMaxLongTermVideoJitterMs
logged max overall video jitter since stream start
Definition config.h:146
sDrmMode CurrentVideoDrmMode
currently used video drm mode
Definition config.h:151
cReassemblyBufferVideo m_videoReassemblyBuffer
video pes reassembly buffer
void ResumeFromPause(void)
Resume playback from pause state.
void Stop(void)
Called by VDR when the plugin is stopped.
bool m_pipUseAlt
use alternative pip position
cReassemblyBufferVideo m_pipReassemblyBuffer
pip pes reassembly buffer
void PipChannelSwap(bool)
cVideoStream * m_pPipStream
pointer to pip video stream
void FlushAudio(void)
Clear all audio data from the decoder and ringbuffer.
void PipSwapPosition(void)
static constexpr int MIN_BUFFER_FILL_LEVEL_THRESHOLD_MS
min buffering threshold in ms
int m_volume
track the volume in the device (for attach)
void SetScreenSize(int, int)
Set the screen size.
virtual void StillPicture(const uchar *, int)
Display the given I-frame as a still picture.
void PipSetSize(void)
bool HaltOpenGlThread(void)
Pause the OpenGL worker thread.
void HandleDisplayModeChange(const sDrmMode &)
Set the display mode.
cSoftOsdProvider * m_pOsdProvider
pointer to cSoftOsdProvider object
cVideoStream * m_pVideoStream
pointer to main video stream
cReassemblyBufferAudio m_audioReassemblyBuffer
audio pes reassembly buffer
int MaxSizeGPUImageCache(void)
Get the maximum GPU image cache size.
void SetDrain(bool)
Set and log the drain state changes.
std::atomic< bool > m_receivedAudio
flag if audio packets have been received
void SetRenderPipSize(void)
cDvbSpuDecoder * m_pSpuDecoder
pointer to spu decoder
int Start(void)
Called by VDR when the plugin is started.
void SetEnableOglOsd(void)
Enables OpenGL/ES Osd.
virtual void GetVideoSize(int &, int &, double &)
Get the video size.
bool IsDetachForced(void)
Returns true, the a detached plugin start was forced.
int PlayAudioPkts(AVPacket *)
Play an audio packet.
virtual void GetOsdSize(int &, int &, double &)
Returns the width, height and aspect ratio the OSD should have.
bool m_initialized
true, if the plugin had a successful Initialize()
void SetEnableHdr(bool)
Enable HDR display mode.
void SetRenderPipActive(bool)
void GetStats(int *, int *, int *)
Get statistics from the renderer.
void SetTrickSpeed(double, bool, bool)
Set the trickspeed in the renderer.
void HaltVideoThreads(void)
Pause the rendering and decoder thread.
void ResetChannelId(void)
Reset the channel ID (restarts audio)
void ResumeVideoThreads(void)
Resume the rendering and decoder thread.
cHardwareDevice * m_pHardwareDevice
pointer to hardware device description
virtual bool CanReplay(void) const
Return true if this device can currently start a replay session.
bool SchedulePlaybackStart(void)
Schedules the playback start.
virtual bool HasDecoder(void) const
Tells whether this device has an MPEG decoder.
virtual void Clear(void)
Clears all video and audio data from the device.
cEventHandler * m_pEventHandler
event handler thread
cPipHandler * m_pPipHandler
pointer to pip handler
void Detach(void)
Detach the device.
void ResumeOpenGlThread(void)
Resume the OpenGL worker thread.
virtual int PlayVideo(const uchar *, int)
Play a video packet of the main videostream.
void ResetPipStream(void)
Resets pip stream and render pipeline.
int GetVideoAudioDelayMs(void)
bool m_logPlaybackStart
virtual bool SetPlayMode(ePlayMode)
Sets the device into the given play mode.
cAudioDecoder * m_pAudioDecoder
pointer to cAudioDecoder object
void SetAudioCodec(enum AVCodecID, AVCodecParameters *, AVRational)
Open an audio codec.
virtual void Play(void)
Sets the device into play mode (after a previous trick mode, or pause)
void EnterState(State)
Actions to be performed when entering a state.
int InitAudio(bool)
Init the audio lazily.
bool CheckPlaybackStartConditions(void)
Check, if all conditions are met to start the playback.
cVideoRender * m_pRender
pointer to cVideoRender object
std::unique_ptr< cStateMachine > m_pStateMachine
void SetOsdSize(int, int)
Set the OSD size.
cSoftHdGrab * m_pGrab
pointer to grabber object
void LeaveState(State)
Actions to be performed when leaving a state.
int OglOsdIsDisabled(void)
Is the OpenGL/ES osd disabled?
cSoftHdDevice(cSoftHdConfig *)
Create the device.
int PlayPipVideo(const uchar *, int)
Play a video packet of the pip videostream.
bool m_needsMakePrimary
true, if device should be made a primary device after attach
virtual void ChannelSwitch(const cDevice *, int, bool)
Monitor a channel switch triggered by VDR (cStatus::ChannelSwitch())
cSoftHdAudio * m_pAudio
pointer to cSoftHdAudio object
cSoftHdConfig * m_pConfig
pointer to cSoftHdConfig object
virtual cRect CanScaleVideo(const cRect &, int taCenter)
Ask the output, if it can scale video.
virtual void SetVolumeDevice(int)
Sets the audio volume on this device (Volume = 0...255).
virtual int64_t GetSTC(void)
Gets the current System Time Counter, which can be used to synchronize audio, video and subtitles.
virtual bool Flush(int=0)
Return true, if the output buffers are empty, false otherwise.
bool IsDetached(void) const
Returns true, if the device is detached.
virtual void TrickSpeed(int, bool)
Sets the device into a mode where replay is done slower.
void SetDecoderFallbackToSw(bool)
Force the decoder to fallback to software if the hardware decoder fails after the configured amount o...
virtual int PlayAudio(const uchar *, int, uchar)
Play an audio packet.
void PipToggle(void)
bool PipIsEnabled(void)
Returns true, if pip is currently enabled.
std::mutex m_sizeMutex
mutex to lock screen size (which is accessed by different threads)
bool Initialize(void)
Initialize the device.
std::atomic< bool > m_receivedVideo
flag if video packets have been received
int PlayVideoPkts(AVPacket *)
Play a video packet.
std::atomic< bool > m_receivedValidAudio
flag if valid audio packets have been received
cJitterTracker m_audioJitterTracker
audio jitter tracker
std::atomic< bool > m_receivedValidVideo
flag if valid video packets have been received
int GetBufferFillLevelThresholdMs()
Returns the buffer fill level threshold in milliseconds.
void HandleStillPicture(const uchar *data, int size)
The still picture data received from VDR can contain multiple PES packets.
bool CheckAudioPlaybackStartConditions(void)
Returns true, if audio playback should start without respect to a/v-sync.
std::atomic< bool > m_draining
true, if the device is in draining mode (waiting for empty buffers)
int PlayVideoInternal(cVideoStream *, cReassemblyBufferVideo *, const uchar *, int, bool, bool)
Play a video packet.
virtual void SetVideoDisplayFormat(eVideoDisplayFormat)
Sets the video display format.
virtual void Freeze(void)
Puts the device into "freeze frame" mode.
void SetDisableOglOsd(void)
Disables OpenGL/ES Osd (called from setup menu or conf)
void OsdDraw(int, int, int, int, int, const uint8_t *, int, int)
Draw an OSD pixmap.
bool m_forceDetached
start the plugin in detached state
void TriggerEvent(const Event &)
With this wrapper function, the device can directly act as an event reveiver.
virtual ~cSoftHdDevice(void)
Destroy the device.
void ResetVideoFilter(void)
Reset the video deinterlace filter.
int m_audioChannelID
current audio channel ID
std::atomic< ChannelSwitchMode > m_channelSwitchMode
current channel switch mode
void PipDisable(void)
virtual void SetVideoFormat(bool)
Set the video format.
void PausePlayback(bool)
Pause the playback.
virtual void Replaying(const cControl *, const char *, const char *, bool)
Monitor a replay start or end.
virtual void MakePrimaryDevice(bool)
Informs a device that it will be the primary device.
void SetVideoCodec(enum AVCodecID, AVCodecParameters *, AVRational)
Open a video codec.
void PipEnable(void)
void SetPassthroughMask(int)
Set the passthrough mask (called from setup menu or conf)
void SetDisableDeint(void)
Disables deinterlacer (called from setup menu or conf)
virtual uchar * GrabImage(int &, bool, int, int, int)
Grabs the currently visible screen image.
void ResumePlayback(void)
Resume playback again.
virtual cSpuDecoder * GetSpuDecoder(void)
Get the device SPU decoder.
cJitterTracker m_videoJitterTracker
video jitter tracker
std::atomic< PlaybackMode > m_playbackMode
current playback mode
void OsdClose(void)
Close the OSD.
int64_t GetFirstAudioPtsMsToPlay()
Calculate the first audio PTS that should be played during synchronized playback.
virtual void ScaleVideo(const cRect &=cRect::Null)
Scale the currently shown video.
int64_t GetFirstVideoPtsMsToPlay()
void SetDisplayMode(int)
Trigger a display mode change event if the mode changed.
bool FastChannelSwitchInTransferMode(void)
void PipChannelChange(int)
virtual bool Poll(cPoller &, int=0)
Return true if the device itself or any of the file handles in poller is ready for further action.
void Attach(void)
Attach the device again.
bool FastChannelSwitchAudioInTransferMode(void)
void ScheduleResyncAtPtsMs(int64_t)
Schedule aa a/v resync in the render thread at the given pts.
bool m_externalPlayerActive
true, if we detached for an external player
Grabbing Processor.
Definition grab.h:79
bool IsActive(void)
Definition grab.h:83
bool Start(bool, int, int, int, int, int)
Start a grab in the video renderer.
Definition grab.cpp:666
uint8_t * Image(void)
Definition grab.h:85
bool ProcessGrab(void)
Start the conversion.
Definition grab.cpp:785
int Size(void)
Definition grab.h:86
void Finish(void)
Clean up.
Definition grab.cpp:700
Plugin OSD provider.
void RequestStopOpenGlThread(void)
Initiate a stop of the OpenGL thread without waiting.
void StopOpenGlThread(void)
Stop the OpenGL thread and cancel it if necessary.
void UnlockOpenGlThread(void)
Unlock the OpenGL thread.
bool LockOpenGlThread(void)
Lock the OpenGL thread.
Video Renderer.
void PushPipFrame(AVFrame *)
Push a PiP frame into the render ringbuffer.
bool IsOutputBufferFull(void)
Check, if the main render output buffer is full.
void SetEnableHdr(bool enable)
void Reset()
Reset the renderer.
void ResetBufferReuseStrategy()
void Halt(void)
void ReInitDisplayMode(void)
Re-Initialize the drm device with current display mode settings.
void OsdClear(void)
Clear the OSD (draw an empty/ transparent OSD)
void OsdDraw(int, int, int, int, int, const uint8_t *, int, int)
Draw an OSD ARGB image.
void ScheduleVideoPlaybackPauseAt(int64_t ptsMs)
void ScheduleResyncAtPtsMs(int64_t ptsMs)
void ClearDecoderToDisplayQueue(void)
Clear (empty) the decoder to display queue.
void ResetDecodingStrategy()
void Exit(void)
Exit and cleanup the renderer.
void EnableOglOsd(void)
void ResetPipBufferReuseStrategy()
void GetStats(int *, int *, int *)
Get some rendering statistics.
void DisplayBlackFrame(void)
Display a black video frame.
void PushMainFrame(AVFrame *)
Push a main frame into the render ringbuffer.
void SetVideoOutputPosition(const cRect &)
Set size and position of the video on the screen.
static constexpr int GetAudioBehindVideoThresholdMs(void)
int64_t GetOutputPtsMs(void)
Get the output PTS in milliseconds.
void SetPipActive(bool on)
void ClearPipDecoderToDisplayQueue(void)
Clear (empty) the decoder to display queue.
cQueue< cDrmBuffer > * GetPipOutputBuffer(void)
void Resume(void)
void SetStillpicture(bool active)
void SchedulePlaybackStartAtPtsMs(int64_t ptsMs)
int64_t GetVideoClock(void)
void SetScheduleAudioResume(bool resume)
void SetDisplayOneFrameThenPause(bool pause)
void SetPipSize(bool)
Set the size and position of the pip window.
void DisableOglOsd(void)
void SetPlaybackPaused(bool pause)
void ResetFrameCounter(void)
Send start condition to video thread.
int Init(void)
Initialize the renderer.
bool CanHandleMode(sDrmMode *)
Wrapper to check, if drm can handle the display mode.
void SetTrickSpeed(double, bool, bool)
Set the trickspeed parameters.
void ResetPipDecodingStrategy()
cQueue< cDrmBuffer > * GetMainOutputBuffer(void)
Video Input Stream.
Definition videostream.h:48
void GetVideoSize(int *, int *, double *)
Get video size and aspect ratio.
bool Drain(void)
Drains the video stream by finalizing any pending data.
bool BuffersEmpty(void)
Return true, if the input AVPacket, videofilter and drm output buffer queues are empty.
void Open(AVCodecID, AVCodecParameters *=nullptr, AVRational={ .num=1,.den=PTSTICKS })
Open a video codec.
void SetDecoderFallbackToSwNumPkts(int numPackets)
Definition videostream.h:89
void StartDecoder()
Start the decoder.
void ClearVdrCoreToDecoderQueue(void)
Clears all video stream data, which is buffered to be decoded.
void ResetInputPts(void)
Definition videostream.h:78
void Resume(void)
Definition videostream.h:66
int GetVideoPacketMax(void)
Definition videostream.h:80
size_t GetAvPacketsFilled(void)
Definition videostream.h:71
void DisableDeint(bool disable)
Definition videostream.h:88
void CloseDecoder(void)
Close the decoder.
void Exit(void)
Exit video stream.
void CancelFilterThread(void)
Stop filter thread.
void FlushDecoder(void)
Flush the decoder.
bool PushAvPacket(AVPacket *avpkt)
Pushes a pre-assembled AVPacket directly to the processing queue.
bool IsInputBufferFull(void)
Definition videostream.h:72
void ResetFilterThreadNeededCheck()
Definition videostream.h:84
int64_t GetInputPtsMs(void)
Definition videostream.h:77
void Halt(void)
Definition videostream.h:65
virtual void SetDeinterlacerDeactivated(bool deactivate)
Definition videostream.h:86
void ResetTrickSpeedFramesSentCounter(void)
Definition videostream.h:75
bool HasInputPts(void)
Definition videostream.h:76
Audio Decoder Header File.
Plugin Configuration Header File.
__attribute__((weak)) union gbm_bo_handle gbm_bo_get_handle_for_plane(struct gbm_bo *bo
Grabbing Interface Header File.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent, DisplayChangeEvent > Event
State
ChannelSwitchMode
Channel switch modes.
Definition config.h:54
static void PrintStreamData(const uchar *payload)
Print the start code, stream id, length, first three bytes (start code) of the payload,...
@ AUDIO_AND_VIDEO
@ VIDEO_ONLY
@ AUDIO_ONLY
@ NONE
@ PLAY
@ STOP
@ BUFFERING
@ TRICK_SPEED
@ DETACHED
@ CONFIG_DISPLAY_MODE_DEFAULT
Definition config.h:43
@ CONFIG_DISPLAY_MODE_FOLLOW_VIDEO_INTERLACED
Definition config.h:45
@ CONFIG_DISPLAY_MODE_FOLLOW_VIDEO
Definition config.h:44
@ CONFIG_DISPLAY_MODE_MANUAL
Definition config.h:46
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:48
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:46
#define LOGERROR
log to LOG_ERR
Definition logger.h:40
#define AV_NOPTS_VALUE
Definition misc.h:76
static const char * Timestamp2StringMs(int64_t ts)
Input PTS is in ms.
Definition misc.h:151
#define LOGWARNING
log to LOG_WARN
Definition logger.h:42
#define LOGGER
shortcut to the logger
Definition logger.h:51
#define LOGFATAL
log to LOG_ERR and abort
Definition logger.h:38
@ L_DRM
drm logs
Definition logger.h:64
@ L_AV_SYNC
audio/video sync logs
Definition logger.h:61
@ L_OSD
osd logs
Definition logger.h:63
@ L_CODEC
codec logs
Definition logger.h:65
@ L_GRAB
grabbing logs
Definition logger.h:73
Describes a hardware device.
Jitter Tracking of Incoming Packets Header File.
Logger Header File.
PES Packet Parser Header File.
PiP (Picture-in-Picture) Interface Header File.
Output Device Header File.
OSD Provider Header File.
Device State Machine and Event Handler Header File.
Holds possible display configurations.
Definition config.h:30
int width
display width
Definition config.h:31
bool interlaced
is this an interlaced mode?
Definition config.h:34
int height
display height
Definition config.h:32
double refreshRateHz
display refresh rate
Definition config.h:33
Video Renderer (Display) Header File.
Video Input Stream Header File.