vdr-plugin-softhddevice-drm-gles 1.7.3-98a2ef7
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/remux.h>
38#include <vdr/thread.h>
39#include <xf86drmMode.h>
40
41#include "audio.h"
42#include "config.h"
43#include "drmdevice.h"
44#include "drmhdr.h"
45#include "grab.h"
46#include "logger.h"
47#include "misc.h"
48#include "queue.h"
49#include "softhddevice.h"
50#include "statemachine.h"
51#include "videorender.h"
52#include "videostream.h"
53
60 : cThread("softhd display"),
61 m_pDevice(device),
62 m_pAudio(m_pDevice->Audio()),
63 m_pConfig(m_pDevice->Config()),
64 m_grabOsd("OSD"),
65 m_grabVideo("VIDEO"),
66 m_grabPip("PIP"),
67 m_pDrmDevice(new cDrmDevice(this, m_pConfig)),
68 m_pHdrMetadata(this),
69 m_enableHdr(m_pConfig->ConfigVideoEnableHDR)
70{
71#ifdef USE_GLES
73 m_bo = nullptr;
74 m_pNextBo = nullptr;
75 m_pOldBo = nullptr;
76#endif
79}
80
85{
86 LOGDEBUG2(L_DRM, "videorender: %s", __FUNCTION__);
87
88 Stop();
89
90 delete m_pDrmDevice;
91}
92
104
115
123
136{
137 if (!frame || dispWidth == 0 || dispHeight == 0)
138 return { dispX, dispY, dispWidth, dispHeight };
139
140 double frameWidth = frame->width > 0 ? frame->width : 1.0;
141 double frameHeight = frame->height > 0 ? frame->height : 1.0;
142 double frameSar = av_q2d(frame->sample_aspect_ratio) ? av_q2d(frame->sample_aspect_ratio) : 1.0;
143 double dispAspect = static_cast<double>(dispWidth) / static_cast<double>(dispHeight);
145
146 double picWidthD = dispWidth;
147 double picHeightD = dispHeight;
148
149 if (dispAspect > frameAspect) {
150 // letterbox horizontally (frame narrower than display)
154 } else {
155 // pillarbox vertically (frame wider than display)
159 }
160
161 // round to the nearest pixel
162 uint64_t picWidth = std::llround(std::max(0.0, picWidthD));
163 uint64_t picHeight = std::llround(std::max(0.0, picHeightD));
164
165 int64_t offsetX = static_cast<int64_t>(dispWidth) - static_cast<int64_t>(picWidth);
166 int64_t offsetY = static_cast<int64_t>(dispHeight) - static_cast<int64_t>(picHeight);
167 uint64_t posX = dispX + static_cast<uint64_t>(std::max<int64_t>(0, offsetX / 2));
168 uint64_t posY = dispY + static_cast<uint64_t>(std::max<int64_t>(0, offsetY / 2));
169
170 return { posX, posY, picWidth, picHeight };
171}
172
179{
180 uint32_t blobID = 0;
181 if (m_pDrmDevice->CreateHdrBlob(&hdrData, sizeof(hdrData), &blobID)) {
182 LOGERROR("videorender: %s: HDR: Failed to create hdr property blob.", __FUNCTION__);
184 LOGERROR("videorender: %s: HDR: Failed to set hdr property", __FUNCTION__);
185 }
186
187 if (blobID)
189
190 if (!m_colorRangeStored) {
194 m_colorRangeStored = true;
195 }
196 }
197}
198
207{
210
212 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
213
217
218 LOGDEBUG2(L_DRM, "videorender: %s: HDR: connector %d -> Colorspace %s", __FUNCTION__,
219 m_pDrmDevice->ConnectorId(), m_pHdrMetadata.GetColorPrimaries() == AVCOL_PRI_BT2020 ? "BT2020_RGB" : "BT709_YCC");
220
221 LOGDEBUG2(L_DRM, "videorender: %s: HDR: plane %d -> COLOR_ENCODING %s, COLOR_RANGE %s (Color %d)", __FUNCTION__,
222 m_pDrmDevice->VideoPlane()->GetId(), m_pHdrMetadata.GetColorPrimaries() == AVCOL_PRI_BT2020 ? "YCBCR_BT20202" : "YCBCR_BT709",
224
227 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
228 }
229
231
232 m_hasDoneHdrModeset = true;
233}
234
262
272{
273 if (!buf)
274 return 1;
275
276 AVFrame *frame = buf->frame;
277
278 if (frame && m_enableHdr) {
282
283 if (!m_pHdrMetadata.Build(&hdrData, frame->color_primaries, frame->color_trc, sd1, sd2)) {
286 }
287 }
288
289 // set display dimensions as default
292 uint64_t dispX = 0;
293 uint64_t dispY = 0;
294
296
297 // get video size and position
298 if (m_videoIsScaled) {
299 dispWidth = m_videoRect.Width();
300 dispHeight = m_videoRect.Height();
301 dispX = m_videoRect.X();
302 dispY = m_videoRect.Y();
303 }
304
305 // fit frame into display
307
308 // now set the plane parameters
309 videoPlane->SetParams(m_pDrmDevice->CrtcId(), buf->Id(),
311 0, 0, buf->Width(), buf->Height());
312
313 buf->SetSizeOnScreen(fittedRect.x, fittedRect.y, fittedRect.w, fittedRect.h); // remember for grab
314
315 return 0;
316}
317
325{
326 if (!m_pBufOsd || !m_pBufOsd->IsDirty())
327 return 1;
328
331
332 // We had draw activity on the osd buffer
333 if (m_pDrmDevice->UseZpos()) {
336 videoPlane->SetPlaneZpos(modeReq);
337 osdPlane->SetPlaneZpos(modeReq);
338
339 LOGDEBUG2(L_DRM, "videorender: %s: SetPlaneZpos: video->plane_id %d -> zpos %" PRIu64 ", osd->plane_id %d -> zpos %" PRIu64 "", __FUNCTION__,
340 videoPlane->GetId(), videoPlane->GetZpos(),
341 osdPlane->GetId(), osdPlane->GetZpos());
342 }
343
348
349 // now set the plane parameters
350 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
351 0, 0, crtcW, crtcH,
352 0, 0, srcW, srcH);
353
354 m_pBufOsd->SetSizeOnScreen(0, 0, crtcW, crtcH); // remember for grab
355
357 return 0;
358}
359
369{
370 if (!buf || !m_pipActive || m_videoIsScaled)
371 return 1;
372
373 AVFrame *frame = buf->frame;
374
375 // set display dimensions as default
378 uint64_t dispX = 0;
379 uint64_t dispY = 0;
380
382
383 // Get video size and position
384 if (m_videoIsScaled) {
385 dispWidth = m_videoRect.Width();
386 dispHeight = m_videoRect.Height();
387 dispX = m_videoRect.X();
388 dispY = m_videoRect.Y();
389 }
390
391 // fit frame into display
393
394 // compute pip window with given scaling and positioning values from menu
395 int64_t centerOffsetX = static_cast<int64_t>(dispWidth) - static_cast<int64_t>(fittedRect.w);
396 int64_t centerOffsetY = static_cast<int64_t>(dispHeight) - static_cast<int64_t>(fittedRect.h);
397 centerOffsetX = std::max<int64_t>(0, centerOffsetX / 2);
398 centerOffsetY = std::max<int64_t>(0, centerOffsetY / 2);
399
400 double crtcWD = fittedRect.w * m_pipScalePercent / 100.0;
401 double crtcHD = fittedRect.h * m_pipScalePercent / 100.0;
402 uint64_t crtcW = std::llround(crtcWD);
403 uint64_t crtcH = std::llround(crtcHD);
404
405 double spaceW = dispWidth - crtcW - centerOffsetX;
407
408 uint64_t crtcX = dispX + std::llround(spaceW * m_pipLeftPercent / 100.0 + centerOffsetX * m_pipScalePercent / 100.0);
409 uint64_t crtcY = dispY + std::llround(spaceH * m_pipTopPercent / 100.0 + centerOffsetY * m_pipScalePercent / 100.0);
410
411 // now set the plane parameters
412 pipPlane->SetParams(m_pDrmDevice->CrtcId(), buf->Id(),
414 0, 0, buf->Width(), buf->Height());
415
416 buf->SetSizeOnScreen(crtcX, crtcY, crtcW, crtcH); // remember for grab
417
418 return 0;
419}
420
430{
431 enum modeSetLevel {
432 MODESET_OSD = (1 << 0),
433 MODESET_VIDEO = (1 << 1),
434 MODESET_PIP = (1 << 2)
435 };
436
437 int modeSet = 0;
443
445 LOGERROR("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
446 return -1;
447 }
448
449 // handle the video plane
450 // If no new video is available, set the old buffer again, if available.
451 // This is necessary to recognize a size-change in SetVideoBuffer().
452 // Though this is not expensive, maybe we should only call that, if size really changed.
454 videoPlane->SetPlane(modeReq);
456// LOGDEBUG2(L_DRM, "videorender: %s: SetPlane Video (fb = %" PRIu64 ")", __FUNCTION__, videoPlane->GetFbId());
457 }
458
459 // handle the pip plane
460 if (pipPlane->GetId()) {
462 pipPlane->SetPlane(modeReq);
463 else
464 pipPlane->ClearPlane(modeReq);
465
467 }
468
469 // handle the osd plane
470 if (!SetOsdBuffer(modeReq)) {
471 osdPlane->SetPlane(modeReq);
473// LOGDEBUG2(L_DRM, "videorender: %s: SetPlane OSD %d (fb = %" PRIu64 ")", __FUNCTION__, m_osdShown, osdPlane->GetFbId());
474 }
475
476 // return without an atomic commit (no video frame and osd activity)
477 if (!modeSet) {
479 return -1;
480 }
481
482 // do the atomic commit
484 if (modeSet & MODESET_OSD)
485 osdPlane->DumpParameters("osd");
487 videoPlane->DumpParameters("video");
488 if (modeSet & MODESET_PIP)
489 pipPlane->DumpParameters("pip");
490
492 LOGERROR("videorender: %s: page flip failed (%d): %m", __FUNCTION__, errno);
493 return -1;
494 }
495
497
498 return 0;
499}
500
509{
510 bool logDropDup = true;
511
516 else
517 logDropDup = false;
518
519 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",
520 (logDropDup && (audioBehindVideoByMs > 0)) ? "Frame duped" : (logDropDup ? "Frame dropped" : "Frames:"),
534}
535
544{
545 if (!frame || !frame->opaque_ref)
546 return 0;
547
548 int *frameFlags = (int *)frame->opaque_ref->data;
549 return *frameFlags;
550}
551
559{
560 int *frameFlags;
561 if (!frame->opaque_ref) {
562 frame->opaque_ref = av_buffer_allocz(sizeof(*frameFlags));
563 if (!frame->opaque_ref) {
564 LOGFATAL("videorender: %s: cannot allocate private frame data", __FUNCTION__);
565 }
566 }
567
568 frameFlags = (int *)frame->opaque_ref->data;
569 *frameFlags = flags;
570}
571
580{
581 if (CommitBuffer(buf, pipBuf) < 0) {
582 // no modesetting was done
583 if (buf && buf->frame)
584 av_frame_free(&buf->frame);
585 if (pipBuf && pipBuf->frame)
586 av_frame_free(&pipBuf->frame);
587
588 return false;
589 } else {
590 if (m_pDrmDevice->HandleEvent() != 0)
591 LOGERROR("threads: display thread: drmHandleEvent failed!");
592
594
595 // now, that we had a successful commit, set the STC if we have a frame. Skip if only the OSD was updated.
596 if (buf && buf->frame) {
597 if (buf->frame->pts != AV_NOPTS_VALUE)
598 SetVideoClock(buf->frame->pts);
599
600 LOGDEBUG2(L_PACKET, "videorender: %s: ID %d: PTS %s", __FUNCTION__, buf->Id(), Timestamp2StringPts(buf->frame->pts));
601 }
602
603 return true;
604 }
605}
606
607/*****************************************************************************
608 * Thread
609 ****************************************************************************/
610
615{
616 LOGDEBUG("videorender: display thread started");
617 while(Running()) {
618 m_mutex.lock();
619
621
622 m_mutex.unlock();
623
625
627 usleep(100); // yield thread. give control also to threads with lower priority.
628 else
629 usleep(1000);
630 }
631 LOGDEBUG("videorender: display thread stopped");
632}
633
638{
639 if (!Active())
640 return;
641
642 LOGDEBUG("videorender: stopping display thread");
643 Cancel(2);
644}
645
655{
657
659
660 // resync, if the video pts reaches the scheduled resync pts
661 // skip the resync, if the difference between the resync-pts and the current video pts is greater
662 // than the AV_RESYNC_BORDER_MS to sort out false positives
665 LOGDEBUG2(L_AV_SYNC, "videorender: resync schedule arrived at %s, current audio pts %s video pts %s",
667 m_eventQueue.push_back(ResyncEvent{});
668 }
670 }
671
672 // Pause was scheduled and we reached this pts now
674 LOGDEBUG2(L_AV_SYNC, "videorender: %s: pause was scheduled at %s)!", __FUNCTION__, Timestamp2StringMs(videoPtsMs));
677 // Resuming audio from pause was scheduled audio needs to catch up video
679 LOGDEBUG2(L_AV_SYNC, "videorender: resuming audio playback: video %s, audio %s", Timestamp2StringMs(videoPtsMs), Timestamp2StringMs(audioPtsMs));
680 m_pAudio->SetPaused(false);
682 // Duplicate frame
684 !skipSync && !m_pAudio->IsPaused()) {
686 m_framePresentationCounter++; // display the current video frame one period longer
687 // Drop frame - max every second frame. Otherwise, the buffer gets drained immediately, if multiple frames in a row are dropped.
691 m_framePresentationCounter--; // skip this pageflip
693
694 return true;
695 }
696
697// LogDroppedDuped(audioPtsMs, videoPtsMs, audioBehindVideoByMs);
698
699 // log AV diff for the first 10 frames and every 10 seconds
700// if (m_startCounter < 10 || m_startCounter % 500 == 0)
701// LOGDEBUG2(L_AV_SYNC, "drop %d, dup %d, total %d audio %s video %s Delay %dms kernel buffer delay %dms diff %dms",
702// m_framesDropped, m_framesDuped, m_startCounter,
703// Timestamp2StringMs(audioPtsMs), Timestamp2StringMs(videoPtsMs),
704// m_pDevice->GetVideoAudioDelayMs(), m_pAudio->GetHardwareOutputDelayMs(), audioBehindVideoByMs);
705
707
708 return false;
709}
710
717{
719
722
724 m_pAudio->SetPaused(false);
725
730 IsTrickSpeed() ||
731 IsStillpicture() ||
736
737 cDrmBuffer *drmBuffer = nullptr;
740
742
743 bool pageFlipDone = false;
744 if (drmBuffer) {
747 int64_t interFrameGapMs = std::abs(PtsToMs(drmBuffer->frame->pts - m_pCurrentlyDisplayed->frame->pts));
749 } else
751 }
752
754 // check if playback shall start
756 drmBuffer->PresentationFinished();
758 return true;
759 } else {
761 m_videoPlaybackPaused = false;
762 }
763 } else if (!m_displayOneFrameThenPause && !IsStillpicture()) {
764 // A/V sync
766 int64_t videoPtsMs = PtsToMs(drmBuffer->frame->pts);
768 // drop frame
769 drmBuffer->PresentationFinished();
770 if (pipBuffer)
771 pipBuffer->PresentationFinished();
773 return true;
774 }
775
778 }
779
781
782 // log channel switch duration
783 if (m_pDevice->Transferring() && ((m_startCounter == 0 && m_displayOneFrameThenPause) || m_startCounter == 1)) {
784 auto now = std::chrono::steady_clock::now();
785 auto channelSwitchDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>
786 (now - LOGGER->GetChannelSwitchStartTime()).count();
787 auto durationSinceFirstPacketMs = std::chrono::duration_cast<std::chrono::milliseconds>
788 (now - LOGGER->GetChannelSwitchDataReceivedTime()).count();
789
790 if (m_startCounter == 0) {
791 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms first frame displayed (+%5dms after first data was received)", channelSwitchDurationMs, durationSinceFirstPacketMs);
792 } else {
794 Skins.Message(mtInfo, cString::sprintf(tr("channel switch done in %ldms (%ldms)"), channelSwitchDurationMs, durationSinceFirstPacketMs));
795 LOGDEBUG2(L_AV_SYNC, "TRACE: +%5dms playback start fired (+%5dms after first data was received)", channelSwitchDurationMs, durationSinceFirstPacketMs);
796 }
797 }
798
802 }
803
806
807 m_lastFrameWasDropped = false;
809
812 // display the current frame again in trick speed mode or for A/V syncing
814 } else if ((m_pBufOsd && m_pBufOsd->IsDirty()) || pipBuffer) {
816 }
817
818 if (pipBuffer) {
821
823 }
824
827
829
830 return pageFlipDone;
831}
832
837{
838 LOGDEBUG2(L_DRM, "videorender: %s: closing, set a black FB", __FUNCTION__);
839
841
845 m_pCurrentlyDisplayed = nullptr;
846 }
847}
848
853{
854 std::lock_guard<std::mutex> lock(m_timebaseMutex);
855
856 return pts * 1000 * av_q2d(m_timebase);
857}
858
863{
864 return m_pDrmDevice->HandleEvent();
865}
866
871{
872 return m_pDrmDevice->CanHandleHdr();
873}
874
875/*****************************************************************************
876 * OSD
877 ****************************************************************************/
878
885{
886 memset((void *)buf->Plane(0), 0, (size_t)(buf->Pitch(0) * buf->Height()));
887}
888
892static void DrawSoftOsd(cDrmBuffer *buf, int xi, int yi,
893 int width, int height, int pitch,
894 const uint8_t * argb, int x, int y)
895{
896 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__,
897 width, height, pitch, argb, x, y, buf->Pitch(0), xi, yi);
898
899 for (int i = 0; i < height; ++i) {
900 memcpy(buf->Plane(0) + x * 4 + (i + y) * buf->Pitch(0),
901 argb + i * pitch, std::min((uint32_t)pitch, buf->Pitch(0)));
902 }
903}
904
905#ifdef USE_GLES
912{
914
918
920 if (!buf) {
921 LOGERROR("videorender: %s: Failed to get GL buffer", __FUNCTION__);
922 return -1;
923 }
924
925 m_pBufOsd = buf;
926
927 // release old buffer for writing again
928 if (m_bo)
930
931 // rotate bos and create and keep bo as m_pOldBo to make it free'able
932 m_pOldBo = m_bo;
933 m_bo = m_pNextBo;
934
935 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));
936
937 return 0;
938}
939#endif
940
945{
946#ifdef USE_GLES
947 if (m_disableOglOsd)
949 else if (EGLSwapBuffers())
950 return;
951#else
953#endif
954
956 m_osdShown = false;
957}
958
972 int width, int height, int pitch,
973 const uint8_t * argb, int x, int y)
974{
975#ifdef USE_GLES
976 if (m_disableOglOsd)
977 DrawSoftOsd(m_pBufOsd, xi, yi, width, height, pitch, argb, x, y);
978 else if (EGLSwapBuffers())
979 return;
980#else
981 DrawSoftOsd(m_pBufOsd, xi, yi, width, height, pitch, argb, x, y);
982#endif
984 m_osdShown = true;
985}
986
990static void ReleaseFrame( __attribute__ ((unused)) void *opaque, uint8_t *data)
991{
993
995}
996
1003{
1004 return m_drmBufferQueue.IsFull();
1005}
1006
1014
1022
1027 AVFrame *frame,
1028 bool trickspeed,
1029 std::atomic<cBufferStrategy*> &bufferReuseStrategy,
1030 std::atomic<cDecodingStrategy*> &decodingStrategy,
1033 bool mainFrame)
1034{
1035 if (bufferReuseStrategy == nullptr) {
1036 if (trickspeed)
1038 else if (frame->format == AV_PIX_FMT_DRM_PRIME)
1040 else
1042 }
1043
1044 if (decodingStrategy == nullptr) {
1045 if (frame->format == AV_PIX_FMT_DRM_PRIME)
1047 else
1049 }
1050
1051 // 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.
1052 //
1053 // @note: This is the only place outside of the display thread, where the video pts is set
1054 // (except setting it to AV_NOPTS_VALUE in cSofthdDevice::Clear() and ChangeState(STOP))
1055 // We only store here, if the stream recently started and the clock wasn't set already in the display thread
1056
1057 if (mainFrame && GetVideoClock() == AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE)
1058 SetVideoClock(frame->pts);
1059
1062
1063 if (!buf)
1064 LOGFATAL("videorender: %s: no free DRM buffer found. This is a bug.", __FUNCTION__);
1065
1066 frame = decodingStrategy.load()->PrepareDrmBuffer(buf, m_pDrmDevice->Fd(), frame);
1067
1068 buf->frame = frame;
1069 buf->SetPresentationPending(true);
1070
1071 drmBufferQueue->Push(buf);
1072}
1073
1082{
1084 return AV_NOPTS_VALUE;
1085
1086 std::lock_guard<std::mutex> lock(m_timebaseMutex);
1087
1088 return GetVideoClock() * 1000 * av_q2d(m_timebase);
1089}
1090
1095{
1096 m_startCounter = 0;
1097 LOGDEBUG("videorender: %s: reset m_startCounter %d TrickSpeed %d", __FUNCTION__, m_startCounter, IsTrickSpeed());
1098}
1099
1104{
1105 m_startCounter = 0;
1106 m_framesDuped = 0;
1107 m_framesDropped = 0;
1110 m_flipCounter = 0;
1111
1112 delete m_decodingStrategy;
1113 m_decodingStrategy = nullptr;
1114}
1115
1123void cVideoRender::SetTrickSpeed(double speed, bool active, bool forward)
1124{
1125 LOGDEBUG2(L_TRICK, "videorender: %s: set trick speed %.3f %s %s", __FUNCTION__, speed, speed > 1.0 ? "fast" : "slow", forward ? "forward" : "backward");
1127 m_trickspeedFactor = speed;
1128 m_trickspeed = active;
1129 m_forwardTrickspeed = forward;
1130}
1131
1140{
1141 if (!IsTrickSpeed())
1142 return 1;
1143
1144 // Calculate the expected number of display refreshes for this frame
1145 double interFrameGapSec = interFrameGapMs / 1000.0;
1146 double refreshPeriodSec = 1.0 / m_refreshRateHz;
1147 int displayCount = std::max(1, static_cast<int>(std::round(interFrameGapSec / refreshPeriodSec / m_trickspeedFactor)));
1148
1149 return displayCount;
1150}
1151
1152/*****************************************************************************
1153 * Grabbing
1154 ****************************************************************************/
1155
1163{
1164 int timeoutMs = 500;
1165 cMutex mutex;
1166 mutex.Lock();
1167 m_startgrab = true;
1168 int err = 0;
1169
1170 if (!m_grabCond.TimedWait(mutex, timeoutMs)) {
1171 LOGWARNING("videorender: %s: timed out after %dms", __FUNCTION__, timeoutMs);
1172 err = 1;
1173 }
1174
1175 std::lock_guard<std::mutex> lock(m_grabMutex);
1176 m_startgrab = false;
1177
1178 return err;
1179}
1180
1187{
1188 std::lock_guard<std::mutex> lock(m_grabMutex);
1189
1190 if (!m_startgrab)
1191 return;
1192
1193 if (m_pBufOsd && m_osdShown) {
1194 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger osd grab arrived", __FUNCTION__);
1196 }
1197
1199 if (pbuf) {
1200 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger video grab arrived", __FUNCTION__);
1202 }
1203
1205 if (pipBuf && grabPip) {
1206 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger pip grab arrived", __FUNCTION__);
1208 }
1209
1210 m_grabCond.Broadcast();
1211}
1212
1217{
1218 std::lock_guard<std::mutex> lock(m_grabMutex);
1219
1220 m_grabOsd.Clear();
1222 m_grabPip.Clear();
1223}
1224
1233{
1237}
1238
1239/*****************************************************************************
1240 * Setup and initialization
1241 ****************************************************************************/
1242
1249void cVideoRender::SetOsdSize(int width, int height)
1250{
1251 m_pDevice->SetOsdSize(width, height);
1252}
1253
1262void cVideoRender::SetScreenSize(int width, int height, double refreshRateHz, bool interlaced)
1263{
1264 m_refreshRateHz = refreshRateHz;
1265 m_pDevice->SetScreenSize(width, height);
1266 m_framesPerFlipCycle = interlaced ? 2 : 1;
1267 m_flipCounter = 0;
1268}
1269
1279
1286{
1287 return m_pDrmDevice->CanHandleMode(mode);
1288}
1289
1299{
1300 // osd fb
1301#ifndef USE_GLES
1302 if (!m_pBufOsd)
1303 m_pBufOsd = new cDrmBuffer();
1304
1306#else
1307 if (m_disableOglOsd) {
1308 if (!m_pBufOsd)
1309 m_pBufOsd = new cDrmBuffer();
1310
1312 }
1313#endif
1314
1315 // black fb
1316 LOGDEBUG2(L_DRM, "videorender: %s: Try to create a black FB", __FUNCTION__);
1319}
1320
1325{
1326 if (m_pDrmDevice->ReInit())
1327 LOGFATAL("videorender: %s: Init drm device failed", __FUNCTION__);
1328
1331 uint32_t modeID = 0;
1332
1334 LOGFATAL("videorender: %s: Failed to create mode property blob.", __FUNCTION__);
1335 if (!(modeReq = m_pDrmDevice->ModeAtomicAlloc())) {
1337 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
1338 }
1339
1342
1346 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
1347 }
1348
1351}
1352
1360{
1361 if (m_pDrmDevice->Init()) {
1362 LOGERROR("videorender: %s: Init drm device failed", __FUNCTION__);
1363 return -1;
1364 }
1365
1366#ifdef USE_GLES
1368 LOGERROR("videorender: %s: Init failed", __FUNCTION__);
1369 Exit();
1370 return -1;
1371 }
1372#endif
1373
1375
1378
1379 InitBuffers();
1380
1381 // save actual modesetting
1383
1386 uint32_t modeID = 0;
1387
1389 LOGFATAL("videorender: %s: Failed to create mode property blob.", __FUNCTION__);
1390 if (!(modeReq = m_pDrmDevice->ModeAtomicAlloc())) {
1392 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
1393 }
1394
1398
1399 // Osd plane
1400 // 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,
1401 // but initially move the OSD behind the VIDEO
1402#ifndef USE_GLES
1403 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
1405 0, 0, m_pBufOsd->Width(), m_pBufOsd->Height());
1406
1407 osdPlane->SetPlane(modeReq);
1408#else
1409 if (m_disableOglOsd) {
1410 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
1412 0, 0, m_pBufOsd->Width(), m_pBufOsd->Height());
1413
1414 osdPlane->SetPlane(modeReq);
1415 }
1416#endif
1417 if (m_pDrmDevice->UseZpos()) {
1418 videoPlane->SetZpos(m_pDrmDevice->ZposOverlay());
1419 videoPlane->SetPlaneZpos(modeReq);
1420#ifdef USE_GLES
1421 osdPlane->SetZpos(m_pDrmDevice->ZposPrimary());
1422 osdPlane->SetPlaneZpos(modeReq);
1423#endif
1424 }
1425
1426 // Black buffer for video plane
1427 videoPlane->SetParams(m_pDrmDevice->CrtcId(), m_bufBlack.Id(),
1429 0, 0, m_bufBlack.Width(), m_bufBlack.Height());
1430
1431 videoPlane->SetPlane(modeReq);
1432
1434#ifndef USE_GLES
1435 osdPlane->DumpParameters("osd");
1436#endif
1437 videoPlane->DumpParameters("video");
1438
1441 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
1442 }
1443
1446
1447 m_osdShown = false;
1448
1449 // init variables page flip
1451
1452 Start();
1453
1454 return 0;
1455}
1456
1463{
1465#ifdef USE_GLES
1466 if (m_disableOglOsd) {
1467 if (m_pBufOsd) {
1468 m_pBufOsd->Destroy();
1469 delete m_pBufOsd;
1470 }
1471 } else {
1472 if (m_pNextBo)
1474 if (m_pOldBo)
1476 }
1477#else
1478 if (m_pBufOsd) {
1479 m_pBufOsd->Destroy();
1480 delete m_pBufOsd;
1481 }
1482#endif
1483}
1484
1489{
1490 LOGDEBUG("videorender: %s", __FUNCTION__);
1491
1492 Reset();
1493 Stop();
1494
1495 // restore saved CRTC configuration
1497
1500
1503
1504 DeleteBuffers();
1505
1506#ifdef USE_GLES
1507 if (!m_disableOglOsd)
1509#endif
1510
1512}
1513
1520{
1521 m_videoRect.Set(rect.Point(), rect.Size());
1522
1523 if (m_videoRect.IsEmpty())
1524 m_videoIsScaled = false;
1525 else
1526 m_videoIsScaled = true;
1527
1528 LOGDEBUG("videorender: %s: %d %d %d %d%s", __FUNCTION__, rect.X(), rect.Y(), rect.Width(), rect.Height(), m_videoIsScaled ? ", video is scaled" : "");
1529}
1530
1535{
1536 for (Event event : m_eventQueue)
1538
1539 m_eventQueue.clear();
1540}
1541
1559
1560/*****************************************************************************
1561 * Buffer reuse strategy: use-once
1562 ****************************************************************************/
1564{
1565 cDrmBuffer *buf = pool->FindUninitilized();
1566
1567 if (buf)
1568 buf->SetDestroyAfterUse(true);
1569
1570 return buf;
1571}
1572
1573/*****************************************************************************
1574 * Buffer reuse strategy: reuse
1575 ****************************************************************************/
1577{
1578 cDrmBuffer *buf = pool->FindByDmaBufHandle(primedata->objects[0].fd);
1579
1580 if (buf)
1581 return buf;
1582 else
1583 return pool->FindUninitilized();
1584}
1585
1587{
1588 cDrmBuffer *buf = pool->FindNoPresentationPending();
1589
1590 if (buf)
1591 return buf;
1592 else
1593 return pool->FindUninitilized();
1594}
1595
1596/*****************************************************************************
1597 * Decoding strategy: software
1598 ****************************************************************************/
1600{
1601 if (!buf->IsDirty()) {
1602 buf->Setup(drmDeviceFd, inframe->width, inframe->height, DRM_FORMAT_NV12, nullptr, true);
1603
1604 int dmaBufHandle;
1606 LOGFATAL("videorender: %s: Failed to retrieve the Prime FD (%d): %m", __FUNCTION__, errno);
1607
1608 buf->SetDmaBufHandle(dmaBufHandle);
1609 }
1610
1611 for (int i = 0; i < inframe->height; ++i)
1612 memcpy(buf->Plane(0) + i * buf->Pitch(0), inframe->data[0] + i * inframe->linesize[0], inframe->linesize[0]);
1613
1614 for (int i = 0; i < inframe->height / 2; ++i)
1615 memcpy(buf->Plane(1) + i * buf->Pitch(1), inframe->data[1] + i * inframe->linesize[1], inframe->linesize[1]);
1616
1617 AVFrame *frame = av_frame_alloc();
1618 frame->pts = inframe->pts;
1619 frame->width = inframe->width;
1620 frame->height = inframe->height;
1621 frame->format = AV_PIX_FMT_DRM_PRIME;
1622 frame->sample_aspect_ratio = inframe->sample_aspect_ratio;
1623
1624 frame->format = AV_PIX_FMT_DRM_PRIME;
1626 primedata->objects[0].fd = buf->DmaBufHandle();
1627 frame->data[0] = (uint8_t *)primedata;
1629
1631
1632 return frame;
1633}
1634
1635/*****************************************************************************
1636 * Decoding strategy: hardware
1637 ****************************************************************************/
1639{
1640 if (!buf->IsDirty()) {
1642 buf->Setup(drmDeviceFd, frame->width, frame->height, DRM_FORMAT_INVALID, primedata, false);
1643 }
1644
1645 return frame;
1646}
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:130
void DestroyAllExcept(cDrmBuffer *)
Destroy all drm buffers except the given one.
DRM Buffer.
Definition drmbuffer.h:33
void MarkClean(void)
Definition drmbuffer.h:53
void SetSizeOnScreen(int x, int y, int w, int h)
Definition drmbuffer.h:87
void Setup(int, uint32_t, uint32_t, uint32_t, AVDRMFrameDescriptor *, bool)
Setup the buffer.
uint32_t Width(void)
Definition drmbuffer.h:45
uint32_t Height(void)
Definition drmbuffer.h:47
void MarkDirty(void)
Definition drmbuffer.h:54
void FillBlack(void)
Color the buffer black.
AVFrame * frame
associated AVFrame
Definition drmbuffer.h:84
int Id(void)
Definition drmbuffer.h:58
bool IsDirty(void)
Definition drmbuffer.h:52
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:85
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:866
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:945
bool IsPaused(void)
Definition audio.h:59
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
Definition audio.cpp:892
int GetUsedRingbufferBytes(void)
Get used bytes in audio ringbuffer.
Definition audio.cpp:746
int ConfigDisableOglOsd
config disable ogl osd
Definition config.h:135
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:123
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 CheckPlaybackStartConditions(void)
Check, if all conditions are met to start the playback.
void SetOsdSize(int, int)
Set the OSD size.
cVideoStream * VideoStream(void)
bool IsDraining(void)
bool CheckAudioPlaybackStartConditions(void)
Returns true, if audio playback should start without respect to a/v-sync.
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
int EGLSwapBuffers(void)
Swap the double buffered EGL buffers.
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
void OsdDraw(int, int, int, int, int, const uint8_t *, int, int)
Draw an OSD ARGB image.
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)
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:71
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: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
static const char * Timestamp2StringPts(int64_t ts)
Input PTS is in PTSTICKS.
Definition misc.h:149
#define EGL_CHECK(stmt)
eglCheckError macro
Definition misc.h:64
@ L_PACKET
decoder packet/frame tracking logs
Definition logger.h:72
@ 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_TRICK
trickspeed logs
Definition logger.h:67
@ L_OPENGL
opengl osd logs
Definition logger.h:69
@ L_GRAB
grabbing logs
Definition logger.h:73
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
static void ClearSoftOsd(cDrmBuffer *buf)
Internal function to clear the software OSD.
static void ReleaseFrame(__attribute__((unused)) void *opaque, uint8_t *data)
Callback free primedata if av_buffer is unreferenced.
static void DrawSoftOsd(cDrmBuffer *buf, int xi, int yi, int width, int height, int pitch, const uint8_t *argb, int x, int y)
Internal function to draw an OSD ARGB image in software.
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.