vdr-plugin-softhddevice-drm-gles 1.6.8-daba64b
videorender.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
17#include <cerrno>
18#include <chrono>
19#include <cinttypes>
20#include <cstdint>
21#include <mutex>
22#include <vector>
23
24#ifdef USE_GLES
25#include <assert.h>
26#include <gbm.h>
27#include <EGL/egl.h>
28#endif
29
30extern "C" {
31#include <libavcodec/avcodec.h>
32#include <libavutil/hwcontext_drm.h>
33}
34
35#include <drm_fourcc.h>
36#include <vdr/osd.h>
37#include <vdr/thread.h>
38#include <xf86drmMode.h>
39
40#include "audio.h"
41#include "config.h"
42#include "drmdevice.h"
43#include "drmhdr.h"
44#include "grab.h"
45#include "logger.h"
46#include "misc.h"
47#include "queue.h"
48#include "softhddevice.h"
49#include "statemachine.h"
50#include "videorender.h"
51#include "videostream.h"
52
59 : cThread("softhd display"),
60 m_pDevice(device),
61 m_pAudio(m_pDevice->Audio()),
62 m_pConfig(m_pDevice->Config()),
63 m_grabOsd("OSD"),
64 m_grabVideo("VIDEO"),
65 m_grabPip("PIP"),
66 m_pDrmDevice(new cDrmDevice(this, m_pConfig)),
67 m_pHdrMetadata(this),
68 m_enableHdr(m_pConfig->ConfigVideoEnableHDR)
69{
70#ifdef USE_GLES
72 m_bo = nullptr;
73 m_pNextBo = nullptr;
74 m_pOldBo = nullptr;
75#endif
76 m_timebase = av_make_q(1, 90000);
78}
79
84{
85 LOGDEBUG2(L_DRM, "videorender: %s", __FUNCTION__);
86
87 Stop();
88
89 delete m_pDrmDevice;
90}
91
103
114
122
135{
136 if (!frame || dispWidth == 0 || dispHeight == 0)
137 return { dispX, dispY, dispWidth, dispHeight };
138
139 double frameWidth = frame->width > 0 ? frame->width : 1.0;
140 double frameHeight = frame->height > 0 ? frame->height : 1.0;
141 double frameSar = av_q2d(frame->sample_aspect_ratio) ? av_q2d(frame->sample_aspect_ratio) : 1.0;
142 double dispAspect = static_cast<double>(dispWidth) / static_cast<double>(dispHeight);
144
145 double picWidthD = dispWidth;
146 double picHeightD = dispHeight;
147
148 if (dispAspect > frameAspect) {
149 // letterbox horizontally (frame narrower than display)
153 } else {
154 // pillarbox vertically (frame wider than display)
158 }
159
160 // round to the nearest pixel
161 uint64_t picWidth = std::llround(std::max(0.0, picWidthD));
162 uint64_t picHeight = std::llround(std::max(0.0, picHeightD));
163
164 int64_t offsetX = static_cast<int64_t>(dispWidth) - static_cast<int64_t>(picWidth);
165 int64_t offsetY = static_cast<int64_t>(dispHeight) - static_cast<int64_t>(picHeight);
166 uint64_t posX = dispX + static_cast<uint64_t>(std::max<int64_t>(0, offsetX / 2));
167 uint64_t posY = dispY + static_cast<uint64_t>(std::max<int64_t>(0, offsetY / 2));
168
169 return { posX, posY, picWidth, picHeight };
170}
171
178{
179 uint32_t blobID = 0;
180 if (m_pDrmDevice->CreateHdrBlob(&hdrData, sizeof(hdrData), &blobID)) {
181 LOGERROR("videorender: %s: HDR: Failed to create hdr property blob.", __FUNCTION__);
183 LOGERROR("videorender: %s: HDR: Failed to set hdr property", __FUNCTION__);
184 }
185
186 if (blobID)
188
189 if (!m_colorRangeStored) {
193 m_colorRangeStored = true;
194 }
195 }
196}
197
206{
209
211 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
212
216
217 LOGDEBUG2(L_DRM, "videorender: %s: HDR: connector %d -> Colorspace %s", __FUNCTION__,
218 m_pDrmDevice->ConnectorId(), m_pHdrMetadata.GetColorPrimaries() == AVCOL_PRI_BT2020 ? "BT2020_RGB" : "BT709_YCC");
219
220 LOGDEBUG2(L_DRM, "videorender: %s: HDR: plane %d -> COLOR_ENCODING %s, COLOR_RANGE %s (Color %d)", __FUNCTION__,
221 m_pDrmDevice->VideoPlane()->GetId(), m_pHdrMetadata.GetColorPrimaries() == AVCOL_PRI_BT2020 ? "YCBCR_BT20202" : "YCBCR_BT709",
223
226 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
227 }
228
230
231 m_hasDoneHdrModeset = true;
232}
233
261
271{
272 if (!buf)
273 return 1;
274
275 AVFrame *frame = buf->frame;
276
277 if (frame && m_enableHdr) {
281
282 if (!m_pHdrMetadata.Build(&hdrData, frame->color_primaries, frame->color_trc, sd1, sd2)) {
285 }
286 }
287
288 // set display dimensions as default
291 uint64_t dispX = 0;
292 uint64_t dispY = 0;
293
295
296 // get video size and position
297 if (m_videoIsScaled) {
298 dispWidth = m_videoRect.Width();
299 dispHeight = m_videoRect.Height();
300 dispX = m_videoRect.X();
301 dispY = m_videoRect.Y();
302 }
303
304 // fit frame into display
306
307 // now set the plane parameters
308 videoPlane->SetParams(m_pDrmDevice->CrtcId(), buf->Id(),
310 0, 0, buf->Width(), buf->Height());
311
312 buf->SetSizeOnScreen(fittedRect.x, fittedRect.y, fittedRect.w, fittedRect.h); // remember for grab
313
314 return 0;
315}
316
324{
325 if (!m_pBufOsd || !m_pBufOsd->IsDirty())
326 return 1;
327
330
331 // We had draw activity on the osd buffer
332 if (m_pDrmDevice->UseZpos()) {
335 videoPlane->SetPlaneZpos(modeReq);
336 osdPlane->SetPlaneZpos(modeReq);
337
338 LOGDEBUG2(L_DRM, "videorender: %s: SetPlaneZpos: video->plane_id %d -> zpos %" PRIu64 ", osd->plane_id %d -> zpos %" PRIu64 "", __FUNCTION__,
339 videoPlane->GetId(), videoPlane->GetZpos(),
340 osdPlane->GetId(), osdPlane->GetZpos());
341 }
342
347
348 // now set the plane parameters
349 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
350 0, 0, crtcW, crtcH,
351 0, 0, srcW, srcH);
352
353 m_pBufOsd->SetSizeOnScreen(0, 0, crtcW, crtcH); // remember for grab
354
356 return 0;
357}
358
368{
369 if (!buf || !m_pipActive || m_videoIsScaled)
370 return 1;
371
372 AVFrame *frame = buf->frame;
373
374 // set display dimensions as default
377 uint64_t dispX = 0;
378 uint64_t dispY = 0;
379
381
382 // Get video size and position
383 if (m_videoIsScaled) {
384 dispWidth = m_videoRect.Width();
385 dispHeight = m_videoRect.Height();
386 dispX = m_videoRect.X();
387 dispY = m_videoRect.Y();
388 }
389
390 // fit frame into display
392
393 // compute pip window with given scaling and positioning values from menu
394 int64_t centerOffsetX = static_cast<int64_t>(dispWidth) - static_cast<int64_t>(fittedRect.w);
395 int64_t centerOffsetY = static_cast<int64_t>(dispHeight) - static_cast<int64_t>(fittedRect.h);
396 centerOffsetX = std::max<int64_t>(0, centerOffsetX / 2);
397 centerOffsetY = std::max<int64_t>(0, centerOffsetY / 2);
398
399 double crtcWD = fittedRect.w * m_pipScalePercent / 100.0;
400 double crtcHD = fittedRect.h * m_pipScalePercent / 100.0;
401 uint64_t crtcW = std::llround(crtcWD);
402 uint64_t crtcH = std::llround(crtcHD);
403
404 double spaceW = dispWidth - crtcW - centerOffsetX;
406
407 uint64_t crtcX = dispX + std::llround(spaceW * m_pipLeftPercent / 100.0 + centerOffsetX * m_pipScalePercent / 100.0);
408 uint64_t crtcY = dispY + std::llround(spaceH * m_pipTopPercent / 100.0 + centerOffsetY * m_pipScalePercent / 100.0);
409
410 // now set the plane parameters
411 pipPlane->SetParams(m_pDrmDevice->CrtcId(), buf->Id(),
413 0, 0, buf->Width(), buf->Height());
414
415 buf->SetSizeOnScreen(crtcX, crtcY, crtcW, crtcH); // remember for grab
416
417 return 0;
418}
419
429{
430 enum modeSetLevel {
431 MODESET_OSD = (1 << 0),
432 MODESET_VIDEO = (1 << 1),
433 MODESET_PIP = (1 << 2)
434 };
435
436 int modeSet = 0;
442
444 LOGERROR("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
445 return -1;
446 }
447
448 // handle the video plane
449 // If no new video is available, set the old buffer again, if available.
450 // This is necessary to recognize a size-change in SetVideoBuffer().
451 // Though this is not expensive, maybe we should only call that, if size really changed.
453 videoPlane->SetPlane(modeReq);
455// LOGDEBUG2(L_DRM, "videorender: %s: SetPlane Video (fb = %" PRIu64 ")", __FUNCTION__, videoPlane->GetFbId());
456 }
457
458 // handle the pip plane
459 if (pipPlane->GetId()) {
461 pipPlane->SetPlane(modeReq);
462 else
463 pipPlane->ClearPlane(modeReq);
464
466 }
467
468 // handle the osd plane
469 if (!SetOsdBuffer(modeReq)) {
470 osdPlane->SetPlane(modeReq);
472// LOGDEBUG2(L_DRM, "videorender: %s: SetPlane OSD %d (fb = %" PRIu64 ")", __FUNCTION__, m_osdShown, osdPlane->GetFbId());
473 }
474
475 // return without an atomic commit (no video frame and osd activity)
476 if (!modeSet) {
478 return -1;
479 }
480
481 // do the atomic commit
483 if (modeSet & MODESET_OSD)
484 osdPlane->DumpParameters("osd");
486 videoPlane->DumpParameters("video");
487 if (modeSet & MODESET_PIP)
488 pipPlane->DumpParameters("pip");
489
491 LOGERROR("videorender: %s: page flip failed (%d): %m", __FUNCTION__, errno);
492 return -1;
493 }
494
496
497 return 0;
498}
499
508{
509 bool logDropDup = true;
510
515 else
516 logDropDup = false;
517
518 LOGDEBUG2(L_AV_SYNC, "%s (%d|%d|%d) Pkts %d Frames %d Rb %d bytes (%dms) PTS: in %s a %s v %s user delay %dms hw delay %dms diff %dms",
519 (logDropDup && (audioBehindVideoByMs > 0)) ? "Frame duped" : (logDropDup ? "Frame dropped" : "Frames:"),
533}
534
543{
544 if (!frame || !frame->opaque_ref)
545 return 0;
546
547 int *frameFlags = (int *)frame->opaque_ref->data;
548 return *frameFlags;
549}
550
558{
559 int *frameFlags;
560 if (!frame->opaque_ref) {
561 frame->opaque_ref = av_buffer_allocz(sizeof(*frameFlags));
562 if (!frame->opaque_ref) {
563 LOGFATAL("videorender: %s: cannot allocate private frame data", __FUNCTION__);
564 }
565 }
566
567 frameFlags = (int *)frame->opaque_ref->data;
568 *frameFlags = flags;
569}
570
579{
580 if (CommitBuffer(buf, pipBuf) < 0) {
581 // no modesetting was done
582 if (buf && buf->frame)
583 av_frame_free(&buf->frame);
584 if (pipBuf && pipBuf->frame)
585 av_frame_free(&pipBuf->frame);
586
587 return false;
588 } else {
589 if (m_pDrmDevice->HandleEvent() != 0)
590 LOGERROR("threads: display thread: drmHandleEvent failed!");
591
593
594 // now, that we had a successful commit, set the STC if we have a frame. Skip if only the OSD was updated.
595 if (buf && buf->frame) {
596 if (buf->frame->pts != AV_NOPTS_VALUE)
597 SetVideoClock(buf->frame->pts);
598
599 LOGDEBUG2(L_PACKET, "videorender: %s: ID %d: PTS %s", __FUNCTION__, buf->Id(), Timestamp2String(buf->frame->pts, 90));
600 }
601
602 return true;
603 }
604}
605
606/*****************************************************************************
607 * Thread
608 ****************************************************************************/
609
614{
615 LOGDEBUG("videorender: display thread started");
616 while(Running()) {
617 m_mutex.lock();
618
620
621 m_mutex.unlock();
622
624
626 usleep(100); // yield thread. give control also to threads with lower priority.
627 else
628 usleep(1000);
629 }
630 LOGDEBUG("videorender: display thread stopped");
631}
632
637{
638 if (!Active())
639 return;
640
641 LOGDEBUG("videorender: stopping display thread");
642 Cancel(2);
643}
644
654{
656
658
659 // resync, if the video pts reaches the scheduled resync pts
660 // skip the resync, if the difference between the resync-pts and the current video pts is greater
661 // than the AV_RESYNC_BORDER_MS to sort out false positives
664 LOGDEBUG2(L_AV_SYNC, "videorender: resync schedule arrived at %s, current audio pts %s video pts %s",
666 m_eventQueue.push_back(ResyncEvent{});
667 }
669 }
670
671 // Pause was scheduled and we reached this pts now
673 LOGDEBUG2(L_AV_SYNC, "videorender: %s: pause was scheduled at %s)!", __FUNCTION__, Timestamp2String(videoPtsMs, 1));
676 // Resuming audio from pause was scheduled audio needs to catch up video
678 LOGDEBUG2(L_AV_SYNC, "videorender: resuming audio playback: video %s, audio %s", Timestamp2String(videoPtsMs, 1), Timestamp2String(audioPtsMs, 1));
679 m_pAudio->SetPaused(false);
681 // Duplicate frame
683 !skipSync && !m_pAudio->IsPaused()) {
685 m_framePresentationCounter++; // display the current video frame one period longer
686 // Drop frame - max every second frame. Otherwise, the buffer gets drained immediately, if multiple frames in a row are dropped.
690 m_framePresentationCounter--; // skip this pageflip
692
693 return true;
694 }
695
696// LogDroppedDuped(audioPtsMs, videoPtsMs, audioBehindVideoByMs);
697
698 // log AV diff for the first 10 frames and every 10 seconds
699// if (m_startCounter < 10 || m_startCounter % 500 == 0)
700// LOGDEBUG2(L_AV_SYNC, "drop %d, dup %d, total %d audio %s video %s Delay %dms kernel buffer delay %dms diff %dms",
701// m_framesDropped, m_framesDuped, m_startCounter,
702// Timestamp2String(audioPtsMs, 1), Timestamp2String(videoPtsMs, 1),
703// m_pDevice->GetVideoAudioDelayMs(), m_pAudio->GetHardwareOutputDelayMs(), audioBehindVideoByMs);
704
706
707 return false;
708}
709
716{
718
721
726 IsTrickSpeed() ||
727 IsStillpicture() ||
732
733 cDrmBuffer *drmBuffer = nullptr;
736
738
739 bool pageFlipDone = false;
740 if (drmBuffer) {
743 int64_t interFrameGapMs = std::abs(PtsToMs(drmBuffer->frame->pts - m_pCurrentlyDisplayed->frame->pts));
745 } else
747 }
748
750 // check if playback shall start
752 drmBuffer->PresentationFinished();
754 return true;
755 } else {
757 m_videoPlaybackPaused = false;
758 }
759 } else if (!m_displayOneFrameThenPause && !IsStillpicture()) {
760 // A/V sync
762 int64_t videoPtsMs = PtsToMs(drmBuffer->frame->pts);
764 // drop frame
765 drmBuffer->PresentationFinished();
766 if (pipBuffer)
767 pipBuffer->PresentationFinished();
769 return true;
770 }
771
774 }
775
777
778 // log channel switch duration
779 if (m_pDevice->Transferring() && ((m_startCounter == 0 && m_displayOneFrameThenPause) || m_startCounter == 1)) {
780 auto now = std::chrono::steady_clock::now();
781 auto channelSwitchDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_pDevice->GetChannelSwitchStartTime()).count();
782 auto durationSinceFirstPacketMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_pDevice->GetChannelSwitchFirstPacketTime()).count();
783
784 if (m_startCounter == 0) {
785 LOGDEBUG("first frame displayed %dms after channel switch, %dms after first packet was received", channelSwitchDurationMs, durationSinceFirstPacketMs);
786 } else {
788 Skins.Message(mtInfo, cString::sprintf(tr("channel switch done in %ldms (%ldms)"), channelSwitchDurationMs, durationSinceFirstPacketMs));
789 LOGDEBUG("playback start fired %dms after channel switch, %dms after first packet was received", channelSwitchDurationMs, durationSinceFirstPacketMs);
790 }
791 }
792
796 }
797
800
801 m_lastFrameWasDropped = false;
803
806 // display the current frame again in trick speed mode or for A/V syncing
808 } else if ((m_pBufOsd && m_pBufOsd->IsDirty()) || pipBuffer) {
810 }
811
812 if (pipBuffer) {
815
817 }
818
821
823
824 return pageFlipDone;
825}
826
831{
832 LOGDEBUG2(L_DRM, "videorender: %s: closing, set a black FB", __FUNCTION__);
833
835
839 m_pCurrentlyDisplayed = nullptr;
840 }
841}
842
847{
848 std::lock_guard<std::mutex> lock(m_timebaseMutex);
849
850 return pts * 1000 * av_q2d(m_timebase);
851}
852
857{
858 return m_pDrmDevice->HandleEvent();
859}
860
865{
866 return m_pDrmDevice->CanHandleHdr();
867}
868
869/*****************************************************************************
870 * OSD
871 ****************************************************************************/
872
877{
878#ifdef USE_GLES
879 if (m_disableOglOsd) {
880 memset((void *)m_pBufOsd->Plane(0), 0,
881 (size_t)(m_pBufOsd->Pitch(0) * m_pBufOsd->Height()));
882 } else {
884
888
890 if (!buf) {
891 LOGERROR("videorender: %s: Failed to get GL buffer", __FUNCTION__);
892 return;
893 }
894
895 m_pBufOsd = buf;
896
897 // release old buffer for writing again
898 if (m_bo)
900
901 // rotate bos and create and keep bo as m_pOldBo to make it free'able
902 m_pOldBo = m_bo;
903 m_bo = m_pNextBo;
904
905 LOGDEBUG2(L_OPENGL, "videorender: %s: eglSwapBuffers m_eglDisplay %p eglSurface %p (%i x %i, %i)", __FUNCTION__, m_pDrmDevice->EglDisplay(), m_pDrmDevice->EglSurface(), buf->Width(), buf->Height(), buf->Pitch(0));
906 }
907#else
908 memset((void *)m_pBufOsd->Plane(0), 0,
909 (size_t)(m_pBufOsd->Pitch(0) * m_pBufOsd->Height()));
910#endif
911
913 m_osdShown = false;
914}
915
916#define MIN(a, b) ((a) < (b) ? (a) : (b))
917
931 int width, int height, int pitch,
932 const uint8_t * argb, int x, int y)
933{
934#ifdef USE_GLES
935 if (m_disableOglOsd) {
936 LOGDEBUG2(L_OSD, "videorender: %s: width %d height %d pitch %d argb %p x %d y %d pitch buf %d xi %d yi %d", __FUNCTION__,
937 width, height, pitch, argb, x, y, m_pBufOsd->Pitch(0), xi, yi);
938 for (int i = 0; i < height; ++i) {
939 memcpy(m_pBufOsd->Plane(0) + x * 4 + (i + y) * m_pBufOsd->Pitch(0),
940 argb + i * pitch, MIN((size_t)pitch, m_pBufOsd->Pitch(0)));
941 }
942 } else {
944
948
950 if (!buf) {
951 LOGERROR("videorender: %s: Failed to get GL buffer", __FUNCTION__);
952 return;
953 }
954
955 m_pBufOsd = buf;
956
957 // release old buffer for writing again
958 if (m_bo)
960
961 // rotate bos and create and keep bo as m_pOldBo to make it free'able
962 m_pOldBo = m_bo;
963 m_bo = m_pNextBo;
964
965 LOGDEBUG2(L_OPENGL, "videorender: %s: eglSwapBuffers eglDisplay %p eglSurface %p (%i x %i, %i)", __FUNCTION__, m_pDrmDevice->EglDisplay(), m_pDrmDevice->EglSurface(), buf->Width(), buf->Height(), buf->Pitch(0));
966 }
967#else
968 // suppress unused variable warnings ...
969 (void) xi;
970 (void) yi;
971 (void) width;
972
973 for (int i = 0; i < height; ++i) {
974 memcpy(m_pBufOsd->Plane(0) + x * 4 + (i + y) * m_pBufOsd->Pitch(0),
975 argb + i * pitch, (size_t)pitch);
976 }
977#endif
979 m_osdShown = true;
980}
981
985static void ReleaseFrame( __attribute__ ((unused)) void *opaque, uint8_t *data)
986{
988
990}
991
998{
999 return m_drmBufferQueue.IsFull();
1000}
1001
1009
1017
1022 AVFrame *frame,
1023 bool trickspeed,
1024 std::atomic<cBufferStrategy*> &bufferReuseStrategy,
1025 std::atomic<cDecodingStrategy*> &decodingStrategy,
1028 bool mainFrame)
1029{
1030 if (bufferReuseStrategy == nullptr) {
1031 if (trickspeed)
1033 else if (frame->format == AV_PIX_FMT_DRM_PRIME)
1035 else
1037 }
1038
1039 if (decodingStrategy == nullptr) {
1040 if (frame->format == AV_PIX_FMT_DRM_PRIME)
1042 else
1044 }
1045
1046 // Store the PTS of the first frame to be presented. The first frame might not have a valid PTS, if gone through a HW deinterlacer.
1047 //
1048 // @note: This is the only place outside of the display thread, where the video pts is set
1049 // (except setting it to AV_NOPTS_VALUE in cSofthdDevice::Clear() and ChangeState(STOP))
1050 // We only store here, if the stream recently started and the clock wasn't set already in the display thread
1051
1052 if (mainFrame && GetVideoClock() == AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE)
1053 SetVideoClock(frame->pts);
1054
1057
1058 if (!buf)
1059 LOGFATAL("videorender: %s: no free DRM buffer found. This is a bug.", __FUNCTION__);
1060
1061 frame = decodingStrategy.load()->PrepareDrmBuffer(buf, m_pDrmDevice->Fd(), frame);
1062
1063 buf->frame = frame;
1064 buf->SetPresentationPending(true);
1065
1066 drmBufferQueue->Push(buf);
1067}
1068
1077{
1079 return AV_NOPTS_VALUE;
1080
1081 std::lock_guard<std::mutex> lock(m_timebaseMutex);
1082
1083 return GetVideoClock() * 1000 * av_q2d(m_timebase);
1084}
1085
1090{
1091 m_startCounter = 0;
1092 LOGDEBUG("videorender: %s: reset m_startCounter %d TrickSpeed %d", __FUNCTION__, m_startCounter, IsTrickSpeed());
1093}
1094
1099{
1100 m_startCounter = 0;
1101 m_framesDuped = 0;
1102 m_framesDropped = 0;
1105 m_flipCounter = 0;
1106
1107 delete m_decodingStrategy;
1108 m_decodingStrategy = nullptr;
1109}
1110
1118void cVideoRender::SetTrickSpeed(double speed, bool active, bool forward)
1119{
1120 LOGDEBUG2(L_TRICK, "videorender: %s: set trick speed %.3f %s %s", __FUNCTION__, speed, speed > 1.0 ? "fast" : "slow", forward ? "forward" : "backward");
1122 m_trickspeedFactor = speed;
1123 m_trickspeed = active;
1124 m_forwardTrickspeed = forward;
1125}
1126
1135{
1136 if (!IsTrickSpeed())
1137 return 1;
1138
1139 // Calculate the expected number of display refreshes for this frame
1140 double interFrameGapSec = interFrameGapMs / 1000.0;
1141 double refreshPeriodSec = 1.0 / m_refreshRateHz;
1142 int displayCount = std::max(1, static_cast<int>(std::round(interFrameGapSec / refreshPeriodSec / m_trickspeedFactor)));
1143
1144 return displayCount;
1145}
1146
1147/*****************************************************************************
1148 * Grabbing
1149 ****************************************************************************/
1150
1158{
1159 int timeoutMs = 500;
1160 cMutex mutex;
1161 mutex.Lock();
1162 m_startgrab = true;
1163 int err = 0;
1164
1165 if (!m_grabCond.TimedWait(mutex, timeoutMs)) {
1166 LOGWARNING("videorender: %s: timed out after %dms", __FUNCTION__, timeoutMs);
1167 err = 1;
1168 }
1169
1170 std::lock_guard<std::mutex> lock(m_grabMutex);
1171 m_startgrab = false;
1172
1173 return err;
1174}
1175
1182{
1183 std::lock_guard<std::mutex> lock(m_grabMutex);
1184
1185 if (!m_startgrab)
1186 return;
1187
1188 if (m_pBufOsd && m_osdShown) {
1189 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger osd grab arrived", __FUNCTION__);
1191 }
1192
1194 if (pbuf) {
1195 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger video grab arrived", __FUNCTION__);
1197 }
1198
1200 if (pipBuf && grabPip) {
1201 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger pip grab arrived", __FUNCTION__);
1203 }
1204
1205 m_grabCond.Broadcast();
1206}
1207
1212{
1213 std::lock_guard<std::mutex> lock(m_grabMutex);
1214
1215 m_grabOsd.Clear();
1217 m_grabPip.Clear();
1218}
1219
1228{
1232}
1233
1234/*****************************************************************************
1235 * Setup and initialization
1236 ****************************************************************************/
1237
1244void cVideoRender::SetOsdSize(int width, int height)
1245{
1246 m_pDevice->SetOsdSize(width, height);
1247}
1248
1257void cVideoRender::SetScreenSize(int width, int height, double refreshRateHz, bool interlaced)
1258{
1259 m_refreshRateHz = refreshRateHz;
1260 m_pDevice->SetScreenSize(width, height);
1261 m_framesPerFlipCycle = interlaced ? 2 : 1;
1262 m_flipCounter = 0;
1263}
1264
1274
1281{
1282 return m_pDrmDevice->CanHandleMode(mode);
1283}
1284
1294{
1295 // osd fb
1296#ifndef USE_GLES
1297 if (!m_pBufOsd)
1298 m_pBufOsd = new cDrmBuffer();
1299
1301#else
1302 if (m_disableOglOsd) {
1303 if (!m_pBufOsd)
1304 m_pBufOsd = new cDrmBuffer();
1305
1307 }
1308#endif
1309
1310 // black fb
1311 LOGDEBUG2(L_DRM, "videorender: %s: Try to create a black FB", __FUNCTION__);
1314}
1315
1320{
1321 if (m_pDrmDevice->ReInit())
1322 LOGFATAL("videorender: %s: Init drm device failed", __FUNCTION__);
1323
1326 uint32_t modeID = 0;
1327
1329 LOGFATAL("videorender: %s: Failed to create mode property blob.", __FUNCTION__);
1330 if (!(modeReq = m_pDrmDevice->ModeAtomicAlloc())) {
1332 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
1333 }
1334
1337
1341 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
1342 }
1343
1346}
1347
1355{
1356 if (m_pDrmDevice->Init()) {
1357 LOGERROR("videorender: %s: Init drm device failed", __FUNCTION__);
1358 return -1;
1359 }
1360
1361#ifdef USE_GLES
1363 LOGERROR("videorender: %s: Init failed", __FUNCTION__);
1364 Exit();
1365 return -1;
1366 }
1367#endif
1368
1370
1373
1374 InitBuffers();
1375
1376 // save actual modesetting
1378
1381 uint32_t modeID = 0;
1382
1384 LOGFATAL("videorender: %s: Failed to create mode property blob.", __FUNCTION__);
1385 if (!(modeReq = m_pDrmDevice->ModeAtomicAlloc())) {
1387 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
1388 }
1389
1393
1394 // Osd plane
1395 // We don't have the m_pBufOsd for OpenGL yet, so we can't set anything. Set src and FbId later when osd was drawn,
1396 // but initially move the OSD behind the VIDEO
1397#ifndef USE_GLES
1398 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
1400 0, 0, m_pBufOsd->Width(), m_pBufOsd->Height());
1401
1402 osdPlane->SetPlane(modeReq);
1403#else
1404 if (m_disableOglOsd) {
1405 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
1407 0, 0, m_pBufOsd->Width(), m_pBufOsd->Height());
1408
1409 osdPlane->SetPlane(modeReq);
1410 }
1411#endif
1412 if (m_pDrmDevice->UseZpos()) {
1413 videoPlane->SetZpos(m_pDrmDevice->ZposOverlay());
1414 videoPlane->SetPlaneZpos(modeReq);
1415#ifdef USE_GLES
1416 osdPlane->SetZpos(m_pDrmDevice->ZposPrimary());
1417 osdPlane->SetPlaneZpos(modeReq);
1418#endif
1419 }
1420
1421 // Black buffer for video plane
1422 videoPlane->SetParams(m_pDrmDevice->CrtcId(), m_bufBlack.Id(),
1424 0, 0, m_bufBlack.Width(), m_bufBlack.Height());
1425
1426 videoPlane->SetPlane(modeReq);
1427
1429#ifndef USE_GLES
1430 osdPlane->DumpParameters("osd");
1431#endif
1432 videoPlane->DumpParameters("video");
1433
1436 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
1437 }
1438
1441
1442 m_osdShown = false;
1443
1444 // init variables page flip
1446
1447 Start();
1448
1449 return 0;
1450}
1451
1458{
1460#ifdef USE_GLES
1461 if (m_disableOglOsd) {
1462 if (m_pBufOsd) {
1463 m_pBufOsd->Destroy();
1464 delete m_pBufOsd;
1465 }
1466 } else {
1467 if (m_pNextBo)
1469 if (m_pOldBo)
1471 }
1472#else
1473 if (m_pBufOsd) {
1474 m_pBufOsd->Destroy();
1475 delete m_pBufOsd;
1476 }
1477#endif
1478}
1479
1484{
1485 LOGDEBUG("videorender: %s", __FUNCTION__);
1486
1487 Reset();
1488 Stop();
1489
1490 // restore saved CRTC configuration
1492
1495
1498
1499 DeleteBuffers();
1500
1501#ifdef USE_GLES
1502 if (!m_disableOglOsd)
1504#endif
1505
1507}
1508
1515{
1516 m_videoRect.Set(rect.Point(), rect.Size());
1517
1518 if (m_videoRect.IsEmpty())
1519 m_videoIsScaled = false;
1520 else
1521 m_videoIsScaled = true;
1522
1523 LOGDEBUG("videorender: %s: %d %d %d %d%s", __FUNCTION__, rect.X(), rect.Y(), rect.Width(), rect.Height(), m_videoIsScaled ? ", video is scaled" : "");
1524}
1525
1530{
1531 for (Event event : m_eventQueue)
1533
1534 m_eventQueue.clear();
1535}
1536
1554
1555/*****************************************************************************
1556 * Buffer reuse strategy: use-once
1557 ****************************************************************************/
1559{
1560 cDrmBuffer *buf = pool->FindUninitilized();
1561
1562 if (buf)
1563 buf->SetDestroyAfterUse(true);
1564
1565 return buf;
1566}
1567
1568/*****************************************************************************
1569 * Buffer reuse strategy: reuse
1570 ****************************************************************************/
1572{
1573 cDrmBuffer *buf = pool->FindByDmaBufHandle(primedata->objects[0].fd);
1574
1575 if (buf)
1576 return buf;
1577 else
1578 return pool->FindUninitilized();
1579}
1580
1582{
1583 cDrmBuffer *buf = pool->FindNoPresentationPending();
1584
1585 if (buf)
1586 return buf;
1587 else
1588 return pool->FindUninitilized();
1589}
1590
1591/*****************************************************************************
1592 * Decoding strategy: software
1593 ****************************************************************************/
1595{
1596 if (!buf->IsDirty()) {
1597 buf->Setup(drmDeviceFd, inframe->width, inframe->height, DRM_FORMAT_NV12, nullptr, true);
1598
1599 int dmaBufHandle;
1601 LOGFATAL("videorender: %s: Failed to retrieve the Prime FD (%d): %m", __FUNCTION__, errno);
1602
1603 buf->SetDmaBufHandle(dmaBufHandle);
1604 }
1605
1606 for (int i = 0; i < inframe->height; ++i)
1607 memcpy(buf->Plane(0) + i * buf->Pitch(0), inframe->data[0] + i * inframe->linesize[0], inframe->linesize[0]);
1608
1609 for (int i = 0; i < inframe->height / 2; ++i)
1610 memcpy(buf->Plane(1) + i * buf->Pitch(1), inframe->data[1] + i * inframe->linesize[1], inframe->linesize[1]);
1611
1612 AVFrame *frame = av_frame_alloc();
1613 frame->pts = inframe->pts;
1614 frame->width = inframe->width;
1615 frame->height = inframe->height;
1616 frame->format = AV_PIX_FMT_DRM_PRIME;
1617 frame->sample_aspect_ratio = inframe->sample_aspect_ratio;
1618
1619 frame->format = AV_PIX_FMT_DRM_PRIME;
1621 primedata->objects[0].fd = buf->DmaBufHandle();
1622 frame->data[0] = (uint8_t *)primedata;
1624
1626
1627 return frame;
1628}
1629
1630/*****************************************************************************
1631 * Decoding strategy: hardware
1632 ****************************************************************************/
1634{
1635 if (!buf->IsDirty()) {
1637 buf->Setup(drmDeviceFd, frame->width, frame->height, 0, primedata, false);
1638 }
1639
1640 return frame;
1641}
Audio Interface Header File.
DRM Buffer: Get a Hardware Buffer to Reuse.
cDrmBuffer * GetBuffer(cDrmBufferPool *, AVDRMFrameDescriptor *) override
DRM Buffer: Get a Software Buffer to Reuse.
cDrmBuffer * GetBuffer(cDrmBufferPool *, AVDRMFrameDescriptor *) override
DRM Buffer: Get a Buffer to Use Once.
Definition videorender.h:97
cDrmBuffer * GetBuffer(cDrmBufferPool *, AVDRMFrameDescriptor *) override
Prepare DRM Buffer for Hardware Decoding.
AVFrame * PrepareDrmBuffer(cDrmBuffer *, int, AVFrame *) override
Prepare DRM Buffer for Software Decoding.
AVFrame * PrepareDrmBuffer(cDrmBuffer *, int, AVFrame *) override
DRM Buffer Pool.
Definition drmbuffer.h:145
void DestroyAllExcept(cDrmBuffer *)
Destroy all drm buffers except the given one.
DRM Buffer.
Definition drmbuffer.h:48
void MarkClean(void)
Definition drmbuffer.h:68
void SetSizeOnScreen(int x, int y, int w, int h)
Definition drmbuffer.h:102
uint32_t Pitch(int idx)
Definition drmbuffer.h:92
void Setup(int, uint32_t, uint32_t, uint32_t, AVDRMFrameDescriptor *, bool)
Setup the buffer.
uint32_t Width(void)
Definition drmbuffer.h:60
uint32_t Height(void)
Definition drmbuffer.h:62
uint8_t * Plane(int idx)
Definition drmbuffer.h:85
void MarkDirty(void)
Definition drmbuffer.h:69
void FillBlack(void)
Color the buffer black.
AVFrame * frame
associated AVFrame
Definition drmbuffer.h:99
int Id(void)
Definition drmbuffer.h:73
bool IsDirty(void)
Definition drmbuffer.h:67
void PresentationFinished(void)
The presentation of this buffer has finished.
void Destroy(void)
Clear and destroy the buffer object and its parameters.
Definition drmbuffer.cpp:90
void SetDestroyAfterUse(bool val)
Definition drmbuffer.h:100
DRM Device.
Definition drmdevice.h:83
int SetConnectorHdrOutputMetadata(drmModeAtomicReqPtr, uint32_t)
uint64_t OsdHeight(void)
Definition drmdevice.h:104
int ModeAtomicCommit(drmModeAtomicReqPtr req, uint32_t flags, void *user_data)
Definition drmdevice.h:135
int InitGbm(void)
Init gbm device and surface.
cDrmBuffer * GetBufFromBo(struct gbm_bo *)
Get a drm buffer from a gbm buffer object.
EGLDisplay EglDisplay(void)
Definition drmdevice.h:118
void ModeAtomicFree(drmModeAtomicReqPtr req)
Definition drmdevice.h:136
int SetConnectorColorspace(drmModeAtomicReqPtr, uint32_t)
int InitEGL(void)
Init EGL context.
int CreateModeBlob(uint32_t *)
drmModeAtomicReqPtr ModeAtomicAlloc(void)
Definition drmdevice.h:134
int Fd(void)
Definition drmdevice.h:95
int GetVideoPlaneColorRange(uint64_t *)
int DestroyHdrBlob(uint32_t)
int SetVideoPlaneColorEncoding(drmModeAtomicReqPtr, uint32_t)
bool HasPipPlane(void)
Definition drmdevice.h:114
EGLSurface EglSurface(void)
Definition drmdevice.h:117
int SetCrtcModeId(drmModeAtomicReqPtr, uint32_t)
uint64_t DisplayHeight(void)
Definition drmdevice.h:102
void ExitGbm(void)
Free gbm device and surface.
int HandleEvent(void)
Poll for a drm event.
bool CanHandleMode(sDrmMode *)
Return true, if the given mode is one of the collected ones.
int DestroyModeBlob(uint32_t)
uint64_t DisplayWidth(void)
Definition drmdevice.h:101
uint64_t ZposPrimary(void)
Definition drmdevice.h:109
int CreateHdrBlob(struct hdr_output_metadata *, size_t, uint32_t *)
struct gbm_surface * GbmSurface(void)
Definition drmdevice.h:121
uint64_t ZposOverlay(void)
Definition drmdevice.h:108
int SetConnectorHdrBlobProperty(uint32_t)
cDrmPlane * PipPlane(void)
Definition drmdevice.h:113
void SaveCrtc(void)
Save information of a CRTC.
int ReInit(void)
Re-Init the drm device with a new connector mode.
uint32_t CrtcId(void)
Definition drmdevice.h:106
void RestoreCrtc(void)
Restore information of a CRTC.
void Close(void)
Close the drm file handle.
cDrmPlane * OsdPlane(void)
Definition drmdevice.h:111
int SetConnectorCrtcId(drmModeAtomicReqPtr)
cDrmPlane * VideoPlane(void)
Definition drmdevice.h:112
uint32_t ConnectorId(void)
Definition drmdevice.h:99
int SetCrtcActive(drmModeAtomicReqPtr, uint32_t)
uint64_t OsdWidth(void)
Definition drmdevice.h:103
int Init(void)
Initiate the drm device.
bool CanHandleHdr(void)
Definition drmdevice.h:130
int SetVideoPlaneColorRange(drmModeAtomicReqPtr, uint32_t)
void InitEvent(void)
Init the event context.
int UseZpos(void)
Definition drmdevice.h:107
DRM Plane.
Definition drmplane.h:23
void FreeProperties(void)
Free the previously filled plane properties.
Definition drmplane.cpp:53
uint32_t GetId(void)
Definition drmplane.h:39
void Clear(void)
Clear the grab buffer (input and output data)
Definition grab.cpp:429
void Set(cDrmBuffer *)
Set the grab buffer and the dimensions how it is presented on the screen.
Definition grab.cpp:443
int GetColorPrimaries(void)
Definition drmhdr.h:113
int Build(struct hdr_output_metadata *, int, int, AVFrameSideData *, AVFrameSideData *)
Build an HDR static metadata blob.
Definition drmhdr.cpp:58
void Clear(void)
Remove all elements from the queue.
Definition queue.h:88
T * Pop(void)
Pop an element from the back of the queue.
Definition queue.h:57
bool IsFull(void)
Check if the queue is full.
Definition queue.h:110
size_t Size(void)
Get the current size of the queue.
Definition queue.h:121
T * Peek(void)
Get a reference to the back element.
Definition queue.h:75
int64_t GetInputPtsMs(void)
Definition audio.h:72
int GetUsedRingbufferMs(void)
Get used ms in audio ringbuffer.
Definition audio.cpp:756
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
Definition audio.cpp:805
int GetAvResyncBorderMs(void)
Definition audio.h:74
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition audio.cpp:420
void SetPaused(bool)
Set audio playback pause state.
Definition audio.cpp:884
bool IsPaused(void)
Definition audio.h:59
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
Definition audio.cpp:831
int GetUsedRingbufferBytes(void)
Get used bytes in audio ringbuffer.
Definition audio.cpp:746
int ConfigDisableOglOsd
config disable ogl osd
Definition config.h:137
int ConfigPipAltTopPercent
0 = aligned to top, 100 = aligned to bottom
Definition config.h:107
int ConfigPipLeftPercent
0 = aligned to left, 100 = aligned to right
Definition config.h:101
int ConfigPipAltLeftPercent
0 = aligned to left, 100 = aligned to right
Definition config.h:106
int ConfigPipAltScalePercent
alternative scale factor of pip video
Definition config.h:105
int ConfigShowChannelSwitchDurationMessage
let the skin show a short message about the channel switch duration
Definition config.h:125
int ConfigPipTopPercent
0 = aligned to top, 100 = aligned to bottom
Definition config.h:102
int ConfigPipScalePercent
scale factor of pip video
Definition config.h:100
int ConfigPipUseAlt
Definition config.h:103
Output Device Implementation.
void SetScreenSize(int, int)
Set the screen size.
int GetVideoAudioDelayMs(void)
void SetDrmCanDisplayPip(bool canDisplay)
bool IsBufferingThresholdReached(void)
Check if the buffering threshold has been reached.
void SetOsdSize(int, int)
Set the OSD size.
std::chrono::steady_clock::time_point GetChannelSwitchFirstPacketTime(void)
cVideoStream * VideoStream(void)
std::chrono::steady_clock::time_point GetChannelSwitchStartTime(void)
bool IsDraining(void)
void TriggerEvent(const Event &)
With this wrapper function, the device can directly act as an event reveiver.
bool IsVideoOnlyPlayback(void)
void SetDisplayMode(int)
Trigger a display mode change event if the mode changed.
void PushPipFrame(AVFrame *)
Push a PiP frame into the render ringbuffer.
void SetFrameFlags(AVFrame *, int)
Set frame flags.
int m_numWrongProgressive
counter for progressive frames sent in an interlaced stream (only used for logging)
int DrmHandleEvent(void)
Wrapper for drmHandleEvent()
bool IsOutputBufferFull(void)
Check, if the main render output buffer is full.
int m_framesDuped
number of frames duplicated
void SetHdrBlob(struct hdr_output_metadata)
Create an hdr blob and set it for the connector.
void InitBuffers(void)
Init the osd and black buffer.
bool m_osdShown
set, if osd is shown currently
cCondVar m_grabCond
condition gets signalled, if renederer finished to clone the grabbed buffers
cDrmBuffer * m_pCurrentlyDisplayed
pointer to currently displayed DRM buffer
int m_pipScalePercent
scale factor for pip
struct gbm_bo * m_pOldBo
pointer to old gbm buffer object (for later free)
std::atomic< bool > m_resumeAudioScheduled
set, if audio resume is scheduled after a pause
int m_flipCounter
page flip counter
struct gbm_bo * m_bo
pointer to current gbm buffer object
void SetScreenSize(int, int, double, bool)
Wrapper to set the screen size in the device.
void Reset()
Reset the renderer.
void PushFrame(AVFrame *, bool, std::atomic< cBufferStrategy * > &, std::atomic< cDecodingStrategy * > &, cQueue< cDrmBuffer > *, cDrmBufferPool *, bool)
Push the frame into the render ringbuffer.
bool CanHandleHdr(void)
Return true, if the device can handle HDR.
void SetDisplayMode(int)
Wrapper to set the display mode.
std::atomic< bool > m_forwardTrickspeed
true, if trickspeed plays forward
std::atomic< bool > m_displayOneFrameThenPause
set, if only one frame shall be displayed and then pause playback
std::vector< Event > m_eventQueue
event queue for incoming events
std::atomic< double > m_refreshRateHz
screen refresh rate in Hz
void CreateGrabBuffers(bool)
Copy current video, osd and pip buffers to dedicated grabbing buffers.
void ReInitDisplayMode(void)
Re-Initialize the drm device with current display mode settings.
int SetVideoBuffer(cDrmBuffer *)
Modesetting for video.
cDrmBufferPool m_pipDrmBufferPool
PIP pool of drm buffers.
cGrabBuffer m_grabVideo
keeps the current grabbed video
void ProcessEvents(void)
Process queued events and forward to event receiver.
int m_framesDropped
number of frames dropped
std::mutex m_grabMutex
mutex around grabbing
void OsdClear(void)
Clear the OSD (draw an empty/ transparent OSD)
std::atomic< int > m_framePresentationCounter
number of times the current frame has to be shown (for slow-motion)
std::atomic< bool > m_enableHdr
hdr is enabled
bool FrameDropNecessary(int64_t, int64_t)
Do the AV Sync.
std::atomic< bool > m_pipActive
true, if pip should be displayed
int SetOsdBuffer(drmModeAtomicReqPtr)
Modesetting for osd.
void ClearDecoderToDisplayQueue(void)
Clear (empty) the decoder to display queue.
std::atomic< bool > m_videoPlaybackPaused
set, if playback is frozen (used for pause)
void Exit(void)
Exit and cleanup the renderer.
void SetColorSpace(drmColorRange)
Set kms color space, color encoding and color range.
std::atomic< int64_t > m_scheduleResyncAtPtsMs
if set, a resync (enter state BUFFERING) will be forced at the given pts
cQueue< cDrmBuffer > m_drmBufferQueue
queue for DRM buffers to be displayed (VIDEO_SURFACES_MAX is defined in thread.h)
cGrabBuffer m_grabPip
keeps the current grabbed pip video
~cVideoRender(void)
Destroy the video renderer.
cRect m_videoRect
rect of the currently displayed video
void GetStats(int *, int *, int *)
Get some rendering statistics.
std::atomic< cDecodingStrategy * > m_pipDecodingStrategy
strategy for decoding setup
std::atomic< cBufferStrategy * > m_bufferReuseStrategy
strategy to select drm buffers
void DisplayBlackFrame(void)
Display a black video frame.
bool IsStillpicture(void)
cSoftHdAudio * m_pAudio
pointer to cSoftHdAudio
AVRational m_timebase
timebase used for pts, set by first RenderFrame()
void PushMainFrame(AVFrame *)
Push a main frame into the render ringbuffer.
std::mutex m_mutex
mutex for thread control
void SetVideoOutputPosition(const cRect &)
Set size and position of the video on the screen.
cVideoRender(cSoftHdDevice *)
Create the video renderer.
int GetFrameFlags(AVFrame *)
Get frame flags.
int m_pipTopPercent
top margin for pip
cDrmBuffer * m_pBufOsd
pointer to osd drm buffer object
cHdrMetadata m_pHdrMetadata
hdr metadata object
std::mutex m_timebaseMutex
mutex used around m_timebase
std::atomic< bool > m_startgrab
internal flag to trigger grabbing
int64_t GetOutputPtsMs(void)
Get the output PTS in milliseconds.
void RestoreColorSpace(void)
Restore color space, color encoding and color range to BT709 and the original color range.
cSoftHdDevice * m_pDevice
pointer to cSoftHdDevice
void DeleteBuffers(void)
Delete the osd and black buffer.
std::atomic< cBufferStrategy * > m_pipBufferReuseStrategy
strategy to select drm buffers
cSoftHdConfig * m_pConfig
pointer to cSoftHdConfig
void ClearPipDecoderToDisplayQueue(void)
Clear (empty) the decoder to display queue.
int CommitBuffer(cDrmBuffer *, cDrmBuffer *)
Commit the frame to the hardware.
static constexpr int AV_SYNC_THRESHOLD_AUDIO_AHEAD_VIDEO_MS
threshold in ms, when to drop video frames to keep audio and video in sync
int TriggerGrab(void)
Trigger a screen grab.
void SetVideoClock(int64_t pts)
cQueue< cDrmBuffer > m_pipDrmBufferQueue
queue for PIP DRM buffers to be displayed (VIDEO_SURFACES_MAX is defined in thread....
int SetPipBuffer(cDrmBuffer *)
Modesetting for pip.
void ClearGrabBuffers(void)
Clear the grab drm buffers.
virtual void Action(void)
Thread loop, which tries to display frames and processes events.
void LogDroppedDuped(int64_t, int64_t, int)
Log A/V sync debug message.
bool DisplayFrame()
Display the frame (video and/or osd)
int m_framesPerFlipCycle
number of pageflips over which a single video frame should be presented 1 in progressive display mode...
bool m_disableOglOsd
set, if ogl osd is disabled
void Stop(void)
Stop the thread.
bool m_colorRangeStored
true, if the original color range was stored
bool IsTrickSpeed(void)
bool m_hasDoneHdrModeset
true, if we ever created an hdr blob and did a modesetting
cDrmBuffer * m_pCurrentlyPipDisplayed
pointer to currently displayed DRM buffer
int64_t GetVideoClock(void)
void OsdDrawARGB(int, int, int, int, int, const uint8_t *, int, int)
Draw an OSD ARGB image.
cDrmDevice * m_pDrmDevice
pointer cDrmDevice object
cGrabBuffer m_grabOsd
keeps the current grabbed osd
std::atomic< double > m_trickspeedFactor
current trick speed
int64_t PtsToMs(int64_t)
Convert a PTS to milliseconds.
std::atomic< int64_t > m_videoPlaybackPauseScheduledAt
if set, video will be paused at the given pts
void SetOsdSize(int, int)
Wrapper to set the osd size in the device.
std::atomic< cDecodingStrategy * > m_decodingStrategy
strategy for decoding setup
std::atomic< bool > m_trickspeed
true, if trickspeed is active
bool m_lastFrameWasDropped
true, if the last frame was dropped
void SetPipSize(bool)
Set the size and position of the pip window.
bool PageFlip(cDrmBuffer *, cDrmBuffer *)
Do the pageflip.
int m_pipLeftPercent
left margin for pip
cDrmBufferPool m_drmBufferPool
pool of drm buffers
drmColorRange m_originalColorRange
initial color range
std::atomic< int64_t > m_schedulePlaybackStartAtPtsMs
if set, frames with PTS older than this will be dropped
static constexpr int AV_SYNC_THRESHOLD_AUDIO_BEHIND_VIDEO_MS
Sync Corridor.
void ResetFrameCounter(void)
Send start condition to video thread.
int m_startCounter
counter for displayed frames, indicates a video start
int Init(void)
Initialize the renderer.
bool CanHandleMode(sDrmMode *)
Wrapper to check, if drm can handle the display mode.
bool m_videoIsScaled
true, if the currently displayed video is scaled
struct gbm_bo * m_pNextBo
pointer to next gbm buffer object (for later free)
int GetFramePresentationCount(int64_t)
Get the number of times the current frame shall be presented in trickspeed mode.
void SetTrickSpeed(double, bool, bool)
Set the trickspeed parameters.
cDrmBuffer m_bufBlack
black drm buffer object
size_t GetAvPacketsFilled(void)
Definition videostream.h:70
Plugin Configuration Header File.
__attribute__((weak)) union gbm_bo_handle gbm_bo_get_handle_for_plane(struct gbm_bo *bo
DRM Device Header File.
HDR (High Dynamic Range) Header File.
Grabbing Interface Header File.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent, DisplayChangeEvent > Event
@ VIDEO
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:47
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:45
#define LOGERROR
log to LOG_ERR
Definition logger.h:39
#define AV_NOPTS_VALUE
Definition misc.h:74
#define LOGWARNING
log to LOG_WARN
Definition logger.h:41
#define LOGFATAL
log to LOG_ERR and abort
Definition logger.h:37
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Nice time-stamp string.
Definition misc.h:127
#define EGL_CHECK(stmt)
eglCheckError macro
Definition misc.h:62
@ L_PACKET
decoder packet/frame tracking logs
Definition logger.h:68
@ L_DRM
drm logs
Definition logger.h:60
@ L_AV_SYNC
audio/video sync logs
Definition logger.h:57
@ L_OSD
osd logs
Definition logger.h:59
@ L_TRICK
trickspeed logs
Definition logger.h:63
@ L_OPENGL
opengl osd logs
Definition logger.h:65
@ L_GRAB
grabbing logs
Definition logger.h:69
Logger Header File.
Misc Functions.
Thread-safe Queue.
Output Device Header File.
Device State Machine and Event Handler Header File.
Holds possible display configurations.
Definition config.h:30
uint64_t y
uint64_t x
uint64_t h
uint64_t w
#define MIN(a, b)
static void ReleaseFrame(__attribute__((unused)) void *opaque, uint8_t *data)
Callback free primedata if av_buffer is unreferenced.
static sRect ComputeFittedRect(AVFrame *frame, uint64_t dispX, uint64_t dispY, uint64_t dispWidth, uint64_t dispHeight)
Fits the video frame into a given area.
Video Renderer (Display) Header File.
@ COLORSPACE_BT2020_RGB
Definition videorender.h:66
@ COLORSPACE_BT709_YCC
Definition videorender.h:65
drmColorRange
Definition videorender.h:74
@ COLORRANGE_FULL
Definition videorender.h:76
@ COLORRANGE_LIMITED
Definition videorender.h:75
@ COLORENCODING_BT2020
Definition videorender.h:71
@ COLORENCODING_BT709
Definition videorender.h:70
Video Input Stream Header File.