vdr-plugin-softhddevice-drm-gles 1.6.4-d0291bb
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 "event.h"
45#include "grab.h"
46#include "logger.h"
47#include "misc.h"
48#include "queue.h"
49#include "softhddevice.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_pDrmDevice(new cDrmDevice(this, m_pConfig->ConfigDisplayResolution, m_pConfig->ConfigDrmDevice)),
64 m_pEventReceiver(device),
65 m_pHdrMetadata(this),
66 m_enableHdr(m_pConfig->ConfigVideoEnableHDR)
67{
68#ifdef USE_GLES
70 m_bo = nullptr;
71 m_pNextBo = nullptr;
72 m_pOldBo = nullptr;
73#endif
74 m_timebase = av_make_q(1, 90000);
76}
77
82{
83 LOGDEBUG2(L_DRM, "videorender: %s", __FUNCTION__);
84
85 Stop();
86
87 delete m_pDrmDevice;
88}
89
101
112
120
133{
134 if (!frame || dispWidth == 0 || dispHeight == 0)
135 return { dispX, dispY, dispWidth, dispHeight };
136
137 double frameWidth = frame->width > 0 ? frame->width : 1.0;
138 double frameHeight = frame->height > 0 ? frame->height : 1.0;
139 double frameSar = av_q2d(frame->sample_aspect_ratio) ? av_q2d(frame->sample_aspect_ratio) : 1.0;
140 double dispAspect = static_cast<double>(dispWidth) / static_cast<double>(dispHeight);
142
143 double picWidthD = dispWidth;
144 double picHeightD = dispHeight;
145
146 if (dispAspect > frameAspect) {
147 // letterbox horizontally (frame narrower than display)
151 } else {
152 // pillarbox vertically (frame wider than display)
156 }
157
158 // round to the nearest pixel
159 uint64_t picWidth = std::llround(std::max(0.0, picWidthD));
160 uint64_t picHeight = std::llround(std::max(0.0, picHeightD));
161
162 int64_t offsetX = static_cast<int64_t>(dispWidth) - static_cast<int64_t>(picWidth);
163 int64_t offsetY = static_cast<int64_t>(dispHeight) - static_cast<int64_t>(picHeight);
164 uint64_t posX = dispX + static_cast<uint64_t>(std::max<int64_t>(0, offsetX / 2));
165 uint64_t posY = dispY + static_cast<uint64_t>(std::max<int64_t>(0, offsetY / 2));
166
167 return { posX, posY, picWidth, picHeight };
168}
169
176{
177 uint32_t blobID = 0;
178 if (m_pDrmDevice->CreateHdrBlob(&hdrData, sizeof(hdrData), &blobID)) {
179 LOGERROR("videorender: %s: HDR: Failed to create hdr property blob.", __FUNCTION__);
181 LOGERROR("videorender: %s: HDR: Failed to set hdr property", __FUNCTION__);
182 }
183
184 if (blobID)
186
187 if (!m_colorRangeStored) {
191 m_colorRangeStored = true;
192 }
193 }
194}
195
204{
207 uint32_t modeID = 0;
208
210 LOGFATAL("videorender: %s: Failed to create mode property blob.", __FUNCTION__);
212 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
213
216 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
220
224
225 LOGDEBUG2(L_DRM, "videorender: %s: HDR: connector %d -> Colorspace %s", __FUNCTION__,
226 m_pDrmDevice->ConnectorId(), m_pHdrMetadata.GetColorPrimaries() == AVCOL_PRI_BT2020 ? "BT2020_RGB" : "BT709_YCC");
227
228 LOGDEBUG2(L_DRM, "videorender: %s: HDR: plane %d -> COLOR_ENCODING %s, COLOR_RANGE %s (Color %d)", __FUNCTION__,
229 m_pDrmDevice->VideoPlane()->GetId(), m_pHdrMetadata.GetColorPrimaries() == AVCOL_PRI_BT2020 ? "YCBCR_BT20202" : "YCBCR_BT709",
231
233 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
234
237
238 m_hasDoneHdrModeset = true;
239}
240
246{
249 uint32_t modeID = 0;
250
252 LOGFATAL("videorender: %s: Failed to create mode property blob.", __FUNCTION__);
254 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
255
257
259 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
260
268
270 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
271
274
275 m_hasDoneHdrModeset = false;
276 m_colorRangeStored = false;
277}
278
288{
289 if (!buf)
290 return 1;
291
292 AVFrame *frame = buf->frame;
293
294 if (frame && m_enableHdr) {
298
299 if (!m_pHdrMetadata.Build(&hdrData, frame->color_primaries, frame->color_trc, sd1, sd2)) {
302 }
303 }
304
305 // set display dimensions as default
308 uint64_t dispX = 0;
309 uint64_t dispY = 0;
310
312
313 // get video size and position
314 if (m_videoIsScaled) {
315 dispWidth = m_videoRect.Width();
316 dispHeight = m_videoRect.Height();
317 dispX = m_videoRect.X();
318 dispY = m_videoRect.Y();
319 }
320
321 // fit frame into display
323
324 // now set the plane parameters
325 videoPlane->SetParams(m_pDrmDevice->CrtcId(), buf->Id(),
327 0, 0, buf->Width(), buf->Height());
328
329 buf->SetSizeOnScreen(fittedRect.x, fittedRect.y, fittedRect.w, fittedRect.h); // remember for grab
330
331 return 0;
332}
333
341{
342 if (!m_pBufOsd || !m_pBufOsd->IsDirty())
343 return 1;
344
347
348 // We had draw activity on the osd buffer
349 if (m_pDrmDevice->UseZpos()) {
352 videoPlane->SetPlaneZpos(modeReq);
353 osdPlane->SetPlaneZpos(modeReq);
354
355 LOGDEBUG2(L_DRM, "videorender: %s: SetPlaneZpos: video->plane_id %d -> zpos %" PRIu64 ", osd->plane_id %d -> zpos %" PRIu64 "", __FUNCTION__,
356 videoPlane->GetId(), videoPlane->GetZpos(),
357 osdPlane->GetId(), osdPlane->GetZpos());
358 }
359
362
363 // now set the plane parameters
364 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
365 0, 0, crtcW, crtcH,
366 0, 0, crtcW, crtcH);
367
368 m_pBufOsd->SetSizeOnScreen(0, 0, crtcW, crtcH); // remember for grab
369
371 return 0;
372}
373
383{
384 if (!buf || !m_pipActive || m_videoIsScaled)
385 return 1;
386
387 AVFrame *frame = buf->frame;
388
389 // set display dimensions as default
392 uint64_t dispX = 0;
393 uint64_t dispY = 0;
394
396
397 // Get video size and position
398 if (m_videoIsScaled) {
399 dispWidth = m_videoRect.Width();
400 dispHeight = m_videoRect.Height();
401 dispX = m_videoRect.X();
402 dispY = m_videoRect.Y();
403 }
404
405 // fit frame into display
407
408 // compute pip window with given scaling and positioning values from menu
409 int64_t centerOffsetX = static_cast<int64_t>(dispWidth) - static_cast<int64_t>(fittedRect.w);
410 int64_t centerOffsetY = static_cast<int64_t>(dispHeight) - static_cast<int64_t>(fittedRect.h);
411 centerOffsetX = std::max<int64_t>(0, centerOffsetX / 2);
412 centerOffsetY = std::max<int64_t>(0, centerOffsetY / 2);
413
414 double crtcWD = fittedRect.w * m_pipScalePercent / 100.0;
415 double crtcHD = fittedRect.h * m_pipScalePercent / 100.0;
416 uint64_t crtcW = std::llround(crtcWD);
417 uint64_t crtcH = std::llround(crtcHD);
418
419 double spaceW = dispWidth - crtcW - centerOffsetX;
421
422 uint64_t crtcX = dispX + std::llround(spaceW * m_pipLeftPercent / 100.0 + centerOffsetX * m_pipScalePercent / 100.0);
423 uint64_t crtcY = dispY + std::llround(spaceH * m_pipTopPercent / 100.0 + centerOffsetY * m_pipScalePercent / 100.0);
424
425 // now set the plane parameters
426 pipPlane->SetParams(m_pDrmDevice->CrtcId(), buf->Id(),
428 0, 0, buf->Width(), buf->Height());
429
430 buf->SetSizeOnScreen(crtcX, crtcY, crtcW, crtcH); // remember for grab
431
432 return 0;
433}
434
444{
445 enum modeSetLevel {
446 MODESET_OSD = (1 << 0),
447 MODESET_VIDEO = (1 << 1),
448 MODESET_PIP = (1 << 2)
449 };
450
451 int modeSet = 0;
457
459 LOGERROR("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
460 return -1;
461 }
462
463 // handle the video plane
464 // If no new video is available, set the old buffer again, if available.
465 // This is necessary to recognize a size-change in SetVideoBuffer().
466 // Though this is not expensive, maybe we should only call that, if size really changed.
468 videoPlane->SetPlane(modeReq);
470// LOGDEBUG2(L_DRM, "videorender: %s: SetPlane Video (fb = %" PRIu64 ")", __FUNCTION__, videoPlane->GetFbId());
471 }
472
473 // handle the pip plane
474 if (pipPlane->GetId()) {
476 pipPlane->SetPlane(modeReq);
477 else
478 pipPlane->ClearPlane(modeReq);
479
481 }
482
483 // handle the osd plane
484 if (!SetOsdBuffer(modeReq)) {
485 osdPlane->SetPlane(modeReq);
487 LOGDEBUG2(L_DRM, "videorender: %s: SetPlane OSD %d (fb = %" PRIu64 ")", __FUNCTION__, m_osdShown, osdPlane->GetFbId());
488 }
489
490 // return without an atomic commit (no video frame and osd activity)
491 if (!modeSet) {
493 return -1;
494 }
495
496 // do the atomic commit
498 if (modeSet & MODESET_OSD)
499 osdPlane->DumpParameters("osd");
501 videoPlane->DumpParameters("video");
502 if (modeSet & MODESET_PIP)
503 pipPlane->DumpParameters("pip");
504
506 LOGERROR("videorender: %s: page flip failed (%d): %m", __FUNCTION__, errno);
507 return -1;
508 }
509
511
512 return 0;
513}
514
523{
524 LOGDEBUG2(L_AV_SYNC, "Frame %s (drop %d, dup %d) Pkts %d Frames %d UsedBytes %d audio %s video %s Delay %dms kernel buffer delay %dms diff %dms",
525 audioBehindVideoByMs > 0 ? "duped" : "dropped",
536
537 if (audioBehindVideoByMs > 0)
539 else
541
542}
543
552{
553 if (!frame || !frame->opaque_ref)
554 return 0;
555
556 int *frameFlags = (int *)frame->opaque_ref->data;
557 return *frameFlags;
558}
559
567{
568 int *frameFlags;
569 if (!frame->opaque_ref) {
570 frame->opaque_ref = av_buffer_allocz(sizeof(*frameFlags));
571 if (!frame->opaque_ref) {
572 LOGFATAL("videorender: %s: cannot allocate private frame data", __FUNCTION__);
573 }
574 }
575
576 frameFlags = (int *)frame->opaque_ref->data;
577 *frameFlags = flags;
578}
579
588{
589 if (CommitBuffer(buf, pipBuf) < 0) {
590 // no modesetting was done
591 if (buf && buf->frame)
592 av_frame_free(&buf->frame);
593 if (pipBuf && pipBuf->frame)
594 av_frame_free(&pipBuf->frame);
595
596 return false;
597 } else {
598 if (m_pDrmDevice->HandleEvent() != 0)
599 LOGERROR("threads: display thread: drmHandleEvent failed!");
600
601 // now, that we had a successful commit, set the STC if we have a frame. Skip if only the OSD was updated.
602 if (buf && buf->frame) {
603 if (buf->frame->pts != AV_NOPTS_VALUE)
604 SetVideoClock(buf->frame->pts);
605
606 LOGDEBUG2(L_PACKET, "videorender: %s: ID %d: PTS %s", __FUNCTION__, buf->Id(), Timestamp2String(buf->frame->pts, 90));
607 }
608
609 return true;
610 }
611}
612
613/*****************************************************************************
614 * Thread
615 ****************************************************************************/
616
621{
622 LOGDEBUG("videorender: display thread started");
623 while(Running()) {
624 m_mutex.lock();
625
627
628 m_mutex.unlock();
629
631
633 usleep(100); // yield thread. give control also to threads with lower priority.
634 else
635 usleep(1000);
636 }
637 LOGDEBUG("videorender: display thread stopped");
638}
639
644{
645 if (!Active())
646 return;
647
648 LOGDEBUG("videorender: stopping display thread");
649 Cancel(2);
650}
651
658{
661
665 IsTrickSpeed() ||
666 IsStillpicture() ||
670
671 cDrmBuffer *drmBuffer = nullptr;
674
676
677 bool pageFlipDone = false;
678 if (drmBuffer) {
681 int64_t interFrameGapMs = std::abs(PtsToMs(drmBuffer->frame->pts - m_pCurrentlyDisplayed->frame->pts));
683 } else
685 }
686
688 // check if playback shall start
690 drmBuffer->PresentationFinished();
691 return true;
692 } else {
694 m_videoPlaybackPaused = false;
695 }
696 } else if (!m_displayOneFrameThenPause && !IsStillpicture()) {
697 // A/V sync
699 int64_t videoPtsMs = PtsToMs(drmBuffer->frame->pts);
700
701 if (audioPtsMs != AV_NOPTS_VALUE) {
703
708
709 LOGDEBUG2(L_AV_SYNC, "videorender: resync schedule arrived at %s, current audio pts %s video pts %s",
711 m_eventQueue.push_back(ResyncEvent{});
713 }
714
716 LOGDEBUG2(L_AV_SYNC, "videorender: %s: pause was scheduled at %s)!", __FUNCTION__, Timestamp2String(videoPtsMs, 1));
719 } else if (m_resumeAudioScheduled && audioBehindVideoByMs >= 0 && !skipSync) { // resume audio from pause
720 LOGDEBUG2(L_AV_SYNC, "videorender: resuming audio playback: video %s, audio %s", Timestamp2String(videoPtsMs, 1), Timestamp2String(audioPtsMs, 1));
721 m_pAudio->SetPaused(false);
725 m_framePresentationCounter++; // display the current video frame one period longer
727 // Drop max every second frame. Otherwise, the buffer gets drained immediately, if multiple frames in a row are dropped.
729
730 if (pipBuf)
731 pipBuf->PresentationFinished();
732
733 drmBuffer->PresentationFinished();
734 m_framePresentationCounter--; // skip this pageflip
736
737 return true;
738 }
739
741 }
742
745 }
746
750 }
751
753 if (m_startCounter == 1 && m_pDevice->Transferring()) {
754 auto now = std::chrono::steady_clock::now();
755 auto channelSwitchDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_pDevice->GetChannelSwitchStartTime()).count();
756 auto durationSinceFirstPacketMs = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_pDevice->GetChannelSwitchFirstPacketTime()).count();
757 LOGDEBUG("channel switch done in %dms, %dms after first packet was received", channelSwitchDurationMs, durationSinceFirstPacketMs);
758 }
759
762
763 m_lastFrameWasDropped = false;
766 // display the current frame again in trick speed mode or for A/V syncing
768 } else if ((m_pBufOsd && m_pBufOsd->IsDirty()) || pipBuf) {
770 }
771
772 if (pipBuf) {
775
777 }
778
781
783
784 return pageFlipDone;
785}
786
791{
792 LOGDEBUG2(L_DRM, "videorender: %s: closing, set a black FB", __FUNCTION__);
793
795
799 m_pCurrentlyDisplayed = nullptr;
800 }
801}
802
807{
808 std::lock_guard<std::mutex> lock(m_timebaseMutex);
809
810 return pts * 1000 * av_q2d(m_timebase);
811}
812
817{
818 return m_pDrmDevice->HandleEvent();
819}
820
825{
826 return m_pDrmDevice->CanHandleHdr();
827}
828
829/*****************************************************************************
830 * OSD
831 ****************************************************************************/
832
837{
838#ifdef USE_GLES
839 if (m_disableOglOsd) {
840 memset((void *)m_pBufOsd->Plane(0), 0,
841 (size_t)(m_pBufOsd->Pitch(0) * m_pBufOsd->Height()));
842 } else {
844
848
850 if (!buf) {
851 LOGERROR("videorender: %s: Failed to get GL buffer", __FUNCTION__);
852 return;
853 }
854
855 m_pBufOsd = buf;
856
857 // release old buffer for writing again
858 if (m_bo)
860
861 // rotate bos and create and keep bo as m_pOldBo to make it free'able
862 m_pOldBo = m_bo;
863 m_bo = m_pNextBo;
864
865 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));
866 }
867#else
868 memset((void *)m_pBufOsd->Plane(0), 0,
869 (size_t)(m_pBufOsd->Pitch(0) * m_pBufOsd->Height()));
870#endif
871
873 m_osdShown = false;
874}
875
876#define MIN(a, b) ((a) < (b) ? (a) : (b))
877
891 int width, int height, int pitch,
892 const uint8_t * argb, int x, int y)
893{
894#ifdef USE_GLES
895 if (m_disableOglOsd) {
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, m_pBufOsd->Pitch(0), xi, yi);
898 for (int i = 0; i < height; ++i) {
899 memcpy(m_pBufOsd->Plane(0) + x * 4 + (i + y) * m_pBufOsd->Pitch(0),
900 argb + i * pitch, MIN((size_t)pitch, m_pBufOsd->Pitch(0)));
901 }
902 } else {
904
908
910 if (!buf) {
911 LOGERROR("videorender: %s: Failed to get GL buffer", __FUNCTION__);
912 return;
913 }
914
915 m_pBufOsd = buf;
916
917 // release old buffer for writing again
918 if (m_bo)
920
921 // rotate bos and create and keep bo as m_pOldBo to make it free'able
922 m_pOldBo = m_bo;
923 m_bo = m_pNextBo;
924
925 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));
926 }
927#else
928 // suppress unused variable warnings ...
929 (void) xi;
930 (void) yi;
931 (void) width;
932
933 for (int i = 0; i < height; ++i) {
934 memcpy(m_pBufOsd->Plane(0) + x * 4 + (i + y) * m_pBufOsd->Pitch(0),
935 argb + i * pitch, (size_t)pitch);
936 }
937#endif
939 m_osdShown = true;
940}
941
945static void ReleaseFrame( __attribute__ ((unused)) void *opaque, uint8_t *data)
946{
948
950}
951
961
969
977
982 AVFrame *frame,
983 bool trickspeed,
984 std::atomic<cBufferStrategy*> &bufferReuseStrategy,
985 std::atomic<cDecodingStrategy*> &decodingStrategy,
988{
989 if (bufferReuseStrategy == nullptr) {
990 if (trickspeed)
992 else if (frame->format == AV_PIX_FMT_DRM_PRIME)
994 else
996 }
997
998 if (decodingStrategy == nullptr) {
999 if (frame->format == AV_PIX_FMT_DRM_PRIME)
1001 else
1003 }
1004
1005 // 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.
1006 //
1007 // @note: This is the only place outside of the display thread, where the video pts is set
1008 // (except setting it to AV_NOPTS_VALUE in cSofthdDevice::Clear() and SetState(STOP))
1009 // We only store here, if the stream recently started and the clock wasn't set already in the display thread
1010
1011 if (GetVideoClock() == AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE)
1012 SetVideoClock(frame->pts);
1013
1016
1017 if (!buf)
1018 LOGFATAL("videorender: %s: no free DRM buffer found. This is a bug.", __FUNCTION__);
1019
1020 frame = decodingStrategy.load()->PrepareDrmBuffer(buf, m_pDrmDevice->Fd(), frame);
1021
1022 buf->frame = frame;
1023 buf->SetPresentationPending(true);
1024
1025 drmBufferQueue->Push(buf);
1026}
1027
1036{
1038 return AV_NOPTS_VALUE;
1039
1040 std::lock_guard<std::mutex> lock(m_timebaseMutex);
1041
1042 return GetVideoClock() * 1000 * av_q2d(m_timebase);
1043}
1044
1049{
1050 m_startCounter = 0;
1051 LOGDEBUG("videorender: %s: reset m_startCounter %d TrickSpeed %d", __FUNCTION__, m_startCounter, IsTrickSpeed());
1052}
1053
1058{
1059 m_startCounter = 0;
1060 m_framesDuped = 0;
1061 m_framesDropped = 0;
1064
1065 delete m_decodingStrategy;
1066 m_decodingStrategy = nullptr;
1067}
1068
1076void cVideoRender::SetTrickSpeed(double speed, bool active, bool forward)
1077{
1078 LOGDEBUG2(L_TRICK, "videorender: %s: set trick speed %.3f %s %s", __FUNCTION__, speed, speed > 1.0 ? "fast" : "slow", forward ? "forward" : "backward");
1080 m_trickspeedFactor = speed;
1081 m_trickspeed = active;
1082 m_forwardTrickspeed = forward;
1083}
1084
1093{
1094 if (!IsTrickSpeed())
1095 return 1;
1096
1097 // Calculate the expected number of display refreshes for this frame
1098 double interFrameGapSec = interFrameGapMs / 1000.0;
1099 double refreshPeriodSec = 1.0 / m_refreshRateHz;
1100 int displayCount = std::max(1, static_cast<int>(std::round(interFrameGapSec / refreshPeriodSec / m_trickspeedFactor)));
1101
1102 return displayCount;
1103}
1104
1105/*****************************************************************************
1106 * Grabbing
1107 ****************************************************************************/
1108
1116{
1117 int timeoutMs = 50;
1118 cMutex mutex;
1119 mutex.Lock();
1120 m_startgrab = true;
1121 int err = 0;
1122
1123 if (!m_grabCond.TimedWait(mutex, timeoutMs)) {
1124 LOGWARNING("videorender: %s: timed out after %dms", __FUNCTION__, timeoutMs);
1125 err = 1;
1126 }
1127
1128 m_startgrab = false;
1129 return err;
1130}
1131
1138{
1139 if (!m_startgrab)
1140 return;
1141
1142 if (m_pBufOsd && m_osdShown) {
1143 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger osd grab arrived", __FUNCTION__);
1146 }
1147
1149 if (pbuf) {
1150 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger video grab arrived", __FUNCTION__);
1153 }
1154
1156 if (pipBuf && grabPip) {
1157 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger pip grab arrived", __FUNCTION__);
1160 }
1161
1162 m_grabCond.Broadcast();
1163}
1164
1177
1186{
1190}
1191
1192/*****************************************************************************
1193 * Setup and initialization
1194 ****************************************************************************/
1195
1203void cVideoRender::SetScreenSize(int width, int height, double refreshRateHz)
1204{
1206 m_pDevice->SetScreenSize(width, height, refreshRateHz);
1207}
1208
1213{
1214 if (m_pDrmDevice->Init())
1215 LOGFATAL("videorender: %s: failed", __FUNCTION__);
1216
1218
1221
1222 // osd FB
1223#ifndef USE_GLES
1224 if (!m_pBufOsd)
1225 m_pBufOsd = new cDrmBuffer();
1226
1228#else
1229 if (m_disableOglOsd) {
1230 if (!m_pBufOsd)
1231 m_pBufOsd = new cDrmBuffer();
1232
1234 }
1235#endif
1236
1237 // black fb
1238 LOGDEBUG2(L_DRM, "videorender: %s: Try to create a black FB", __FUNCTION__);
1241
1242 // save actual modesetting
1244
1247 uint32_t modeID = 0;
1248
1250 LOGFATAL("videorender: %s: Failed to create mode property blob.", __FUNCTION__);
1252 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
1253
1257
1258 // Osd plane
1259 // 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,
1260 // but initially move the OSD behind the VIDEO
1261#ifndef USE_GLES
1262 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
1264 0, 0, m_pBufOsd->Width(), m_pBufOsd->Height());
1265
1266 osdPlane->SetPlane(modeReq);
1267#else
1268 if (m_disableOglOsd) {
1269 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
1271 0, 0, m_pBufOsd->Width(), m_pBufOsd->Height());
1272
1273 osdPlane->SetPlane(modeReq);
1274 }
1275#endif
1276 if (m_pDrmDevice->UseZpos()) {
1277 videoPlane->SetZpos(m_pDrmDevice->ZposOverlay());
1278 videoPlane->SetPlaneZpos(modeReq);
1279#ifdef USE_GLES
1280 osdPlane->SetZpos(m_pDrmDevice->ZposPrimary());
1281 osdPlane->SetPlaneZpos(modeReq);
1282#endif
1283 }
1284
1285 // Black buffer for video plane
1286 videoPlane->SetParams(m_pDrmDevice->CrtcId(), m_bufBlack.Id(),
1288 0, 0, m_bufBlack.Width(), m_bufBlack.Height());
1289
1290 videoPlane->SetPlane(modeReq);
1291
1293#ifndef USE_GLES
1294 osdPlane->DumpParameters("osd");
1295#endif
1296 videoPlane->DumpParameters("video");
1297
1299 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
1300 }
1301
1303
1304 m_osdShown = false;
1305
1306 // init variables page flip
1308
1309 Start();
1310}
1311
1316{
1317 LOGDEBUG("videorender: %s", __FUNCTION__);
1318
1321
1322 Reset();
1323 Stop();
1324
1325 // restore saved CRTC configuration
1327
1330
1331 videoPlane->FreeProperties();
1332 osdPlane->FreeProperties();
1333
1335#ifdef USE_GLES
1336 if (m_disableOglOsd) {
1337 if (m_pBufOsd) {
1338 m_pBufOsd->Destroy();
1339 delete m_pBufOsd;
1340 }
1341 } else {
1342 if (m_pNextBo)
1344 if (m_pOldBo)
1346 }
1347#else
1348 if (m_pBufOsd) {
1349 m_pBufOsd->Destroy();
1350 delete m_pBufOsd;
1351 }
1352#endif
1353
1355}
1356
1363{
1364 m_videoRect.Set(rect.Point(), rect.Size());
1365
1366 if (m_videoRect.IsEmpty())
1367 m_videoIsScaled = false;
1368 else
1369 m_videoIsScaled = true;
1370
1371 LOGDEBUG("videorender: %s: %d %d %d %d%s", __FUNCTION__, rect.X(), rect.Y(), rect.Width(), rect.Height(), m_videoIsScaled ? ", video is scaled" : "");
1372}
1373
1378{
1379 for (Event event : m_eventQueue)
1381
1382 m_eventQueue.clear();
1383}
1384
1402
1403/*****************************************************************************
1404 * Buffer reuse strategy: use-once
1405 ****************************************************************************/
1407{
1408 cDrmBuffer *buf = pool->FindUninitilized();
1409
1410 if (buf)
1411 buf->SetDestroyAfterUse(true);
1412
1413 return buf;
1414}
1415
1416/*****************************************************************************
1417 * Buffer reuse strategy: reuse
1418 ****************************************************************************/
1420{
1421 cDrmBuffer *buf = pool->FindByDmaBufHandle(primedata->objects[0].fd);
1422
1423 if (buf)
1424 return buf;
1425 else
1426 return pool->FindUninitilized();
1427}
1428
1430{
1431 cDrmBuffer *buf = pool->FindNoPresentationPending();
1432
1433 if (buf)
1434 return buf;
1435 else
1436 return pool->FindUninitilized();
1437}
1438
1439/*****************************************************************************
1440 * Decoding strategy: software
1441 ****************************************************************************/
1443{
1444 if (!buf->IsDirty()) {
1445 buf->Setup(drmDeviceFd, inframe->width, inframe->height, DRM_FORMAT_NV12, nullptr, true);
1446
1447 int dmaBufHandle;
1449 LOGFATAL("videorender: %s: Failed to retrieve the Prime FD (%d): %m", __FUNCTION__, errno);
1450
1451 buf->SetDmaBufHandle(dmaBufHandle);
1452 }
1453
1454 for (int i = 0; i < inframe->height; ++i)
1455 memcpy(buf->Plane(0) + i * buf->Pitch(0), inframe->data[0] + i * inframe->linesize[0], inframe->linesize[0]);
1456
1457 for (int i = 0; i < inframe->height / 2; ++i)
1458 memcpy(buf->Plane(1) + i * buf->Pitch(1), inframe->data[1] + i * inframe->linesize[1], inframe->linesize[1]);
1459
1460 AVFrame *frame = av_frame_alloc();
1461 frame->pts = inframe->pts;
1462 frame->width = inframe->width;
1463 frame->height = inframe->height;
1464 frame->format = AV_PIX_FMT_DRM_PRIME;
1465 frame->sample_aspect_ratio = inframe->sample_aspect_ratio;
1466
1467 frame->format = AV_PIX_FMT_DRM_PRIME;
1469 primedata->objects[0].fd = buf->DmaBufHandle();
1470 frame->data[0] = (uint8_t *)primedata;
1472
1474
1475 return frame;
1476}
1477
1478/*****************************************************************************
1479 * Decoding strategy: hardware
1480 ****************************************************************************/
1482{
1483 if (!buf->IsDirty()) {
1485 buf->Setup(drmDeviceFd, frame->width, frame->height, 0, primedata, false);
1486 }
1487
1488 return frame;
1489}
Audio and Alsa Interface Header File.
virtual void OnEventReceived(const Event &)=0
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.
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:139
void DestroyAllExcept(cDrmBuffer *)
Destroy all drm buffers except the given one.
DRM Buffer.
Definition drmbuffer.h:46
void MarkClean(void)
Definition drmbuffer.h:67
void SetSizeOnScreen(int x, int y, int w, int h)
Definition drmbuffer.h:97
uint32_t Pitch(int idx)
Definition drmbuffer.h:88
void Setup(int, uint32_t, uint32_t, uint32_t, AVDRMFrameDescriptor *, bool)
Setup the buffer.
uint32_t Width(void)
Definition drmbuffer.h:59
uint32_t Height(void)
Definition drmbuffer.h:61
uint8_t * Plane(int idx)
Definition drmbuffer.h:81
void MarkDirty(void)
Definition drmbuffer.h:68
void FillBlack(void)
Color the buffer black.
AVFrame * frame
associated AVFrame
Definition drmbuffer.h:94
int Id(void)
Definition drmbuffer.h:72
bool IsDirty(void)
Definition drmbuffer.h:66
void PresentationFinished(void)
The presentation of this buffer has finished.
void Destroy(void)
Clear and destroy the buffer object and its parameters.
void SetDestroyAfterUse(bool val)
Definition drmbuffer.h:95
DRM Device.
Definition drmdevice.h:43
int SetConnectorHdrOutputMetadata(drmModeAtomicReqPtr, uint32_t)
int ModeAtomicCommit(drmModeAtomicReqPtr req, uint32_t flags, void *user_data)
Definition drmdevice.h:86
cDrmBuffer * GetBufFromBo(struct gbm_bo *)
Get a drm buffer from a gbm buffer object.
EGLDisplay EglDisplay(void)
Definition drmdevice.h:70
void ModeAtomicFree(drmModeAtomicReqPtr req)
Definition drmdevice.h:87
int SetConnectorColorspace(drmModeAtomicReqPtr, uint32_t)
int CreateModeBlob(uint32_t *)
drmModeAtomicReqPtr ModeAtomicAlloc(void)
Definition drmdevice.h:85
int Fd(void)
Definition drmdevice.h:49
int GetVideoPlaneColorRange(uint64_t *)
int DestroyHdrBlob(uint32_t)
int SetVideoPlaneColorEncoding(drmModeAtomicReqPtr, uint32_t)
bool HasPipPlane(void)
Definition drmdevice.h:66
EGLSurface EglSurface(void)
Definition drmdevice.h:69
int SetCrtcModeId(drmModeAtomicReqPtr, uint32_t)
uint64_t DisplayHeight(void)
Definition drmdevice.h:56
int HandleEvent(void)
Poll for a drm event.
int DestroyModeBlob(uint32_t)
uint64_t DisplayWidth(void)
Definition drmdevice.h:55
uint64_t ZposPrimary(void)
Definition drmdevice.h:61
int CreateHdrBlob(struct hdr_output_metadata *, size_t, uint32_t *)
struct gbm_surface * GbmSurface(void)
Definition drmdevice.h:73
uint64_t ZposOverlay(void)
Definition drmdevice.h:60
int SetConnectorHdrBlobProperty(uint32_t)
cDrmPlane * PipPlane(void)
Definition drmdevice.h:65
void SaveCrtc(void)
Save information of a CRTC.
uint32_t CrtcId(void)
Definition drmdevice.h:58
void RestoreCrtc(void)
Restore information of a CRTC.
void Close(void)
Close the drm file handle.
cDrmPlane * OsdPlane(void)
Definition drmdevice.h:63
int SetConnectorCrtcId(drmModeAtomicReqPtr)
cDrmPlane * VideoPlane(void)
Definition drmdevice.h:64
uint32_t ConnectorId(void)
Definition drmdevice.h:53
int SetCrtcActive(drmModeAtomicReqPtr, uint32_t)
int Init(void)
Initiate the drm device.
bool CanHandleHdr(void)
Definition drmdevice.h:82
int SetVideoPlaneColorRange(drmModeAtomicReqPtr, uint32_t)
void InitEvent(void)
Init the event context.
int UseZpos(void)
Definition drmdevice.h:59
DRM Plane.
Definition drmplane.h:23
uint32_t GetId(void)
Definition drmplane.h:39
void FreeDrmBuf(void)
Free the grabbed drm buffer.
Definition grab.cpp:301
void SetDrmBuf(cDrmBuffer *)
Set the grab buffer and the dimensions how it is presented on the screen.
Definition grab.cpp:320
cDrmBuffer * GetDrmBuf(void)
Definition grab.h:70
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 IsEmpty(void)
Check if the queue is empty.
Definition queue.h:99
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
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
Definition audio.cpp:1043
int GetAvResyncBorderMs(void)
Definition audio.h:72
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition audio.cpp:698
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
Definition audio.cpp:1004
void SetPaused(bool)
Set audio playback pause state.
Definition audio.cpp:1121
bool IsPaused(void)
Definition audio.h:58
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
Definition audio.cpp:1065
int ConfigDisableOglOsd
config disable ogl osd
Definition config.h:97
int ConfigPipAltTopPercent
0 = aligned to top, 100 = aligned to bottom
Definition config.h:69
int ConfigPipLeftPercent
0 = aligned to left, 100 = aligned to right
Definition config.h:63
int ConfigPipAltLeftPercent
0 = aligned to left, 100 = aligned to right
Definition config.h:68
int ConfigPipAltScalePercent
alternative scale factor of pip video
Definition config.h:67
int ConfigPipTopPercent
0 = aligned to top, 100 = aligned to bottom
Definition config.h:64
int ConfigPipScalePercent
scale factor of pip video
Definition config.h:62
int ConfigPipUseAlt
Definition config.h:65
Output Device Implementation.
int GetVideoAudioDelayMs(void)
void SetDrmCanDisplayPip(bool canDisplay)
bool IsBufferingThresholdReached(void)
Check if the buffering threshold has been reached.
std::chrono::steady_clock::time_point GetChannelSwitchFirstPacketTime(void)
cVideoStream * VideoStream(void)
std::chrono::steady_clock::time_point GetChannelSwitchStartTime(void)
void SetScreenSize(int, int, double)
Set the screen size.
bool IsVideoOnlyPlayback(void)
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.
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
struct gbm_bo * m_bo
pointer to current gbm buffer object
void Reset()
Reset the renderer.
bool CanHandleHdr(void)
Return true, if the device can handle HDR.
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
void CreateGrabBuffers(bool)
Copy current video, osd and pip buffers to dedicated grabbing buffers.
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
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
double m_refreshRateHz
screen refresh rate in Hz
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.
void Init(void)
Initialize the renderer.
std::atomic< bool > m_videoPlaybackPaused
set, if playback is frozen (used for pause)
void PushFrame(AVFrame *, bool, std::atomic< cBufferStrategy * > &, std::atomic< cDecodingStrategy * > &, cQueue< cDrmBuffer > *, cDrmBufferPool *)
Push the frame into the render ringbuffer.
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
bool m_startgrab
internal flag to trigger grabbing
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
int64_t GetOutputPtsMs(void)
Get the output PTS in milliseconds.
cSoftHdDevice * m_pDevice
pointer to cSoftHdDevice
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.
int TriggerGrab(void)
Trigger a screen grab.
void SetVideoClock(int64_t pts)
IEventReceiver * m_pEventReceiver
pointer to event receiver
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)
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
void SetScreenSize(int, int, double)
Wrapper to set the screen size in the device.
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
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
void RestoreColorSpace()
Restore color space, color encoding and color range to BT709 and the original color range.
void ResetFrameCounter(void)
Send start condition to video thread.
int m_startCounter
counter for displayed frames, indicates a video start
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.
State Machine and Event Header File.
Grabbing Interface Header File.
#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
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent > Event
Definition event.h:82
#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
@ VIDEO
Definition event.h:28
Logger Header File.
Misc Functions.
Thread-safe Queue.
Output Device Header File.
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:69
@ COLORSPACE_BT709_YCC
Definition videorender.h:68
drmColorRange
Definition videorender.h:77
@ COLORRANGE_FULL
Definition videorender.h:79
@ COLORRANGE_LIMITED
Definition videorender.h:78
#define AV_SYNC_THRESHOLD_AUDIO_BEHIND_VIDEO_MS
threshold in ms, when to duplicate video frames to keep audio and video in sync
Definition videorender.h:64
@ COLORENCODING_BT2020
Definition videorender.h:74
@ COLORENCODING_BT709
Definition videorender.h:73
#define AV_SYNC_THRESHOLD_AUDIO_AHEAD_VIDEO_MS
threshold in ms, when to drop video frames to keep audio and video in sync
Definition videorender.h:65
Video Input Stream Header File.