vdr-plugin-softhddevice-drm-gles 1.4.0
videorender.cpp
Go to the documentation of this file.
1
25#ifndef __USE_GNU
26#define __USE_GNU
27#endif
28
29#include <algorithm>
30#include <atomic>
31#include <functional>
32
33#include <stdbool.h>
34#include <unistd.h>
35
36#include <inttypes.h>
37
38#include <libintl.h>
39
40#ifdef USE_GLES
41#include <assert.h>
42#endif
43#include <pthread.h>
44#include <errno.h>
45#include <fcntl.h>
46#include <string.h>
47#include <sys/mman.h>
48#include <drm_fourcc.h>
49
50#include "logger.h"
51
52extern "C" {
53#include <libavcodec/avcodec.h>
54#include <libavutil/hwcontext_drm.h>
55#include <libavutil/pixdesc.h>
56#include <libavfilter/buffersink.h>
57#include <libavfilter/buffersrc.h>
58#include <libavutil/opt.h>
59}
60
61#include "misc.h"
62#include "buf2rgb.h"
63
64#include "videorender.h"
65#include "audio.h"
66#include "drm.h"
67#include "threads.h"
68#include "grab.h"
69#include "drmdevice.h"
70
71/*****************************************************************************
72 * cVideoRender class
73 ****************************************************************************/
74
81{
82 m_pEventReceiver = device;
83 m_pDevice = device;
87
88 m_startgrab = false;
90 m_videoIsScaled = false;
91
92 m_trickSpeed = 0;
93 m_trickForward = true;
94
95 m_timebase = av_make_q(1, 90000);
96
97 m_pBufOsd = nullptr;
98
99#ifdef USE_GLES
100 m_disableOglOsd = m_pConfig->ConfigDisableOglOsd;
101 m_bo = nullptr;
102 m_pNextBo = nullptr;
103 m_pOldBo = nullptr;
104#endif
106}
107
112{
113 LOGDEBUG2(L_DRM, "videorender: %s", __FUNCTION__);
114
116 delete m_pDisplayThread;
117
118 delete m_pDrmDevice;
119}
120
125{
128
131}
132
137{
140
141 m_pCurrentlyPipDisplayed = nullptr;
142}
143
144struct sRect {
145 uint64_t x;
146 uint64_t y;
147 uint64_t w;
148 uint64_t h;
149};
150
162static sRect ComputeFittedRect(AVFrame *frame, uint64_t dispX, uint64_t dispY, uint64_t dispWidth, uint64_t dispHeight)
163{
164 if (!frame || dispWidth == 0 || dispHeight == 0)
165 return { dispX, dispY, dispWidth, dispHeight };
166
167 double frameWidth = frame->width > 0 ? frame->width : 1.0;
168 double frameHeight = frame->height > 0 ? frame->height : 1.0;
169 double frameSar = av_q2d(frame->sample_aspect_ratio) ? av_q2d(frame->sample_aspect_ratio) : 1.0;
170 double dispAspect = static_cast<double>(dispWidth) / static_cast<double>(dispHeight);
171 double frameAspect = frameWidth / frameHeight * frameSar;
172
173 double picWidthD = dispWidth;
174 double picHeightD = dispHeight;
175
176 if (dispAspect > frameAspect) {
177 // letterbox horizontally (frame narrower than display)
178 picWidthD = dispHeight * frameAspect;
179 if (picWidthD <= 0 || picWidthD > dispWidth)
180 picWidthD = dispWidth;
181 } else {
182 // pillarbox vertically (frame wider than display)
183 picHeightD = dispWidth / frameAspect;
184 if (picHeightD <= 0 || picHeightD > dispHeight)
185 picHeightD = dispHeight;
186 }
187
188 // round to the nearest pixel
189 uint64_t picWidth = std::llround(std::max(0.0, picWidthD));
190 uint64_t picHeight = std::llround(std::max(0.0, picHeightD));
191
192 int64_t offsetX = static_cast<int64_t>(dispWidth) - static_cast<int64_t>(picWidth);
193 int64_t offsetY = static_cast<int64_t>(dispHeight) - static_cast<int64_t>(picHeight);
194 uint64_t posX = dispX + static_cast<uint64_t>(std::max<int64_t>(0, offsetX / 2));
195 uint64_t posY = dispY + static_cast<uint64_t>(std::max<int64_t>(0, offsetY / 2));
196
197 return { posX, posY, picWidth, picHeight };
198}
199
206{
207 AVFrame *frame = buf ? buf->frame : nullptr;
208
209 // set display dimensions as default
210 uint64_t dispWidth = m_pDrmDevice->DisplayWidth();
211 uint64_t dispHeight = m_pDrmDevice->DisplayHeight();
212 uint64_t dispX = 0;
213 uint64_t dispY = 0;
214
215 cDrmPlane *videoPlane = m_pDrmDevice->VideoPlane();
216
217 // get video size and position
218 if (m_videoIsScaled) {
219 dispWidth = m_videoRect.Width();
220 dispHeight = m_videoRect.Height();
221 dispX = m_videoRect.X();
222 dispY = m_videoRect.Y();
223 }
224
225 // fit frame into display
226 sRect fittedRect = ComputeFittedRect(frame, dispX, dispY, dispWidth, dispHeight);
227
228 // now set the plane parameters
229 videoPlane->SetParams(m_pDrmDevice->CrtcId(), buf->Id(),
230 fittedRect.x, fittedRect.y, fittedRect.w, fittedRect.h,
231 0, 0, buf->Width(), buf->Height());
232
233 // set dimensions for grab early, because we might skip this at the next frame
234 m_lastVideoGrab.Set(fittedRect.x, fittedRect.y, fittedRect.w, fittedRect.h);
235}
236
243int cVideoRender::SetOsdBuffer(drmModeAtomicReqPtr modeReq)
244{
245 cDrmPlane *videoPlane = m_pDrmDevice->VideoPlane();
246 cDrmPlane *osdPlane = m_pDrmDevice->OsdPlane();
247
248 // We had draw activity on the osd buffer
249 if (m_pBufOsd && m_pBufOsd->IsDirty()) {
250 if (m_pDrmDevice->UseZpos()) {
253 videoPlane->SetPlaneZpos(modeReq);
254 osdPlane->SetPlaneZpos(modeReq);
255
256 LOGDEBUG2(L_DRM, "videorender: %s: SetPlaneZpos: video->plane_id %d -> zpos %" PRIu64 ", osd->plane_id %d -> zpos %" PRIu64 "", __FUNCTION__,
257 videoPlane->GetId(), videoPlane->GetZpos(),
258 osdPlane->GetId(), osdPlane->GetZpos());
259 }
260
261 uint64_t crtcW = m_osdShown ? m_pBufOsd->Width() : 0;
262 uint64_t crtcH = m_osdShown ? m_pBufOsd->Height() : 0;
263
264 // now set the plane parameters
265 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
266 0, 0, crtcW, crtcH,
267 0, 0, crtcW, crtcH);
268
270 return 0;
271 }
272
273 return 1;
274}
275
282{
283 AVFrame *frame = buf ? buf->frame : nullptr;
284
285 // set display dimensions as default
286 uint64_t dispWidth = m_pDrmDevice->DisplayWidth();
287 uint64_t dispHeight = m_pDrmDevice->DisplayHeight();
288 uint64_t dispX = 0;
289 uint64_t dispY = 0;
290
291 cDrmPlane *pipPlane = m_pDrmDevice->PipPlane();
292
293 // Get video size and position
294 if (m_videoIsScaled) {
295 dispWidth = m_videoRect.Width();
296 dispHeight = m_videoRect.Height();
297 dispX = m_videoRect.X();
298 dispY = m_videoRect.Y();
299 }
300
301 // fit frame into display
302 sRect fittedRect = ComputeFittedRect(frame, dispX, dispY, dispWidth, dispHeight);
303
304 // compute pip window with given scaling and positioning values from menu
305 int64_t centerOffsetX = static_cast<int64_t>(dispWidth) - static_cast<int64_t>(fittedRect.w);
306 int64_t centerOffsetY = static_cast<int64_t>(dispHeight) - static_cast<int64_t>(fittedRect.h);
307 centerOffsetX = std::max<int64_t>(0, centerOffsetX / 2);
308 centerOffsetY = std::max<int64_t>(0, centerOffsetY / 2);
309
310 double crtcWD = fittedRect.w * m_pipScalePercent / 100.0;
311 double crtcHD = fittedRect.h * m_pipScalePercent / 100.0;
312 uint64_t crtcW = std::llround(crtcWD);
313 uint64_t crtcH = std::llround(crtcHD);
314
315 double spaceW = dispWidth - crtcW - centerOffsetX;
316 double spaceH = dispHeight - crtcH - centerOffsetY;
317
318 uint64_t crtcX = dispX + std::llround(spaceW * m_pipLeftPercent / 100.0 + centerOffsetX * m_pipScalePercent / 100.0);
319 uint64_t crtcY = dispY + std::llround(spaceH * m_pipTopPercent / 100.0 + centerOffsetY * m_pipScalePercent / 100.0);
320
321 // now set the plane parameters
322 pipPlane->SetParams(m_pDrmDevice->CrtcId(), buf->Id(),
323 crtcX, crtcY, crtcW, crtcH,
324 0, 0, buf->Width(), buf->Height());
325
326 // set dimensions for grab early, because we might skip this at the next frame
327 m_lastPipGrab.Set(crtcX, crtcY, crtcW, crtcH);
328}
329
338{
339 if (m_pBufOsd && m_osdShown) {
340 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger osd grab arrived", __FUNCTION__);
341 cDrmBuffer *osdBuf = new cDrmBuffer(m_pBufOsd);
342 // dimensions should be the size on screen
344 m_grabOsd.SetBuf(osdBuf);
345 }
346
347 cDrmBuffer *pbuf = buf ? buf : (m_pCurrentlyDisplayed ? m_pCurrentlyDisplayed : NULL);
348 if (pbuf) {
349 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger video grab arrived", __FUNCTION__);
350 cDrmBuffer *videoBuf = new cDrmBuffer(pbuf);
351 // use dimensions which have been set earlier
353 m_grabVideo.SetBuf(videoBuf);
354 }
355
356 cDrmBuffer *pipBuf = pip ? pip : (m_pCurrentlyPipDisplayed ? m_pCurrentlyPipDisplayed : NULL);
357 if (pipBuf) {
358 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger pip grab arrived", __FUNCTION__);
359 cDrmBuffer *pipVideoBuf = new cDrmBuffer(pipBuf);
360 // use dimensions which have been set earlier
362 m_grabPip.SetBuf(pipVideoBuf);
363 }
364
365 m_grabCond.Broadcast();
366}
367
377{
378 enum modeSetLevel {
379 MODESET_OSD = (1 << 0),
380 MODESET_VIDEO = (1 << 1),
381 MODESET_PIP = (1 << 2)
382 };
383
384 int modeSet = 0;
385 int fdDrm = m_pDrmDevice->Fd();
386 cDrmPlane *videoPlane = m_pDrmDevice->VideoPlane();
387 cDrmPlane *osdPlane = m_pDrmDevice->OsdPlane();
388 cDrmPlane *pipPlane = m_pDrmDevice->PipPlane();
389 drmModeAtomicReqPtr modeReq;
390 uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT;
391
392 if (!(modeReq = drmModeAtomicAlloc())) {
393 LOGERROR("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
394 return -1;
395 }
396
397 // handle the video plane
398 if (buf) {
399 SetVideoBuffer(buf);
400 videoPlane->SetPlane(modeReq);
401 modeSet |= MODESET_VIDEO;
402// LOGDEBUG2(L_DRM, "videorender: %s: SetPlane Video (fb = %" PRIu64 ")", __FUNCTION__, videoPlane->GetFbId());
403 } else if (m_pCurrentlyDisplayed) {
404 // If no new video is available, set the old buffer again, if available.
405 // This is necessary to recognize a size-change in SetVideoBuffer().
406 // Though this is not expensive, maybe we should only call that, if size really changed.
408 videoPlane->SetPlane(modeReq);
409 modeSet |= MODESET_VIDEO;
410 }
411
412 // handle the pip plane
413 if (IsPipActive() && pip) {
414 SetPipBuffer(pip);
415 pipPlane->SetPlane(modeReq);
416 modeSet |= MODESET_PIP;
417 } else if (IsPipActive() && m_pCurrentlyPipDisplayed) {
419 pipPlane->SetPlane(modeReq);
420 modeSet |= MODESET_PIP;
421 } else {
422 pipPlane->ClearPlane(modeReq);
423 modeSet |= MODESET_PIP;
424 }
425
426 // handle the osd plane
427 if (!SetOsdBuffer(modeReq)) {
428 osdPlane->SetPlane(modeReq);
429 modeSet |= MODESET_OSD;
430 LOGDEBUG2(L_DRM, "videorender: %s: SetPlane OSD %d (fb = %" PRIu64 ")", __FUNCTION__, m_osdShown, osdPlane->GetFbId());
431 }
432
433 // grab, if requested
434 if (m_startgrab)
435 Grab(buf, pip);
436
437 // return without an atomic commit (no video frame and osd activity)
438 if (!modeSet) {
439 drmModeAtomicFree(modeReq);
440 return -1;
441 }
442
443 // do the atomic commit
444 if (drmModeAtomicCommit(fdDrm, modeReq, flags, NULL) != 0) {
445 if (modeSet & MODESET_OSD)
446 osdPlane->DumpParameters("osd");
447 if (modeSet & MODESET_VIDEO)
448 videoPlane->DumpParameters("video");
449 if (modeSet & MODESET_PIP)
450 pipPlane->DumpParameters("pip");
451
452 drmModeAtomicFree(modeReq);
453 LOGERROR("videorender: %s: page flip failed (%d): %m", __FUNCTION__, errno);
454 return -1;
455 }
456
457 drmModeAtomicFree(modeReq);
458
459 return 0;
460}
461
469void cVideoRender::LogDroppedDuped(int64_t audioPtsMs, int64_t videoPtsMs, int audioBehindVideoByMs)
470{
471 LOGDEBUG2(L_AV_SYNC, "Frame %s (drop %d, dup %d) Pkts %d Frames %d UsedBytes %d audio %s video %s Delay %dms diff %dms",
472 audioBehindVideoByMs > 0 ? "duped" : "dropped",
478 Timestamp2String(audioPtsMs, 1),
479 Timestamp2String(videoPtsMs, 1),
481 audioBehindVideoByMs);
482
483 if (audioBehindVideoByMs > 0)
485 else
487
488}
489
498{
499 if (!frame || !frame->opaque_ref)
500 return 0;
501
502 int *frameFlags = (int *)frame->opaque_ref->data;
503 return *frameFlags;
504}
505
512void cVideoRender::SetFrameFlags(AVFrame *frame, int flags)
513{
514 int *frameFlags;
515 if (!frame->opaque_ref) {
516 frame->opaque_ref = av_buffer_allocz(sizeof(*frameFlags));
517 if (!frame->opaque_ref) {
518 LOGFATAL("videorender: %s: cannot allocate private frame data", __FUNCTION__);
519 }
520 }
521
522 frameFlags = (int *)frame->opaque_ref->data;
523 *frameFlags = flags;
524}
525
534{
535 if (CommitBuffer(buf, pipBuf) < 0) {
536 // no modesetting was done
537 if (buf && buf->frame)
538 av_frame_free(&buf->frame);
539 if (pipBuf && pipBuf->frame)
540 av_frame_free(&pipBuf->frame);
541
542 return false;
543 } else {
544 if (m_pDrmDevice->HandleEvent() != 0)
545 LOGERROR("threads: display thread: drmHandleEvent failed!");
546
547 // now, that we had a successful commit, set the STC if we have a frame. Skip if only the OSD was updated.
548 if (buf && buf->frame) {
549 if (buf->frame->pts != AV_NOPTS_VALUE)
550 SetVideoClock(buf->frame->pts);
551
552 LOGDEBUG2(L_PACKET, "videorender: %s: ID %d: PTS %s", __FUNCTION__, buf->Id(), Timestamp2String(buf->frame->pts, 90));
553 }
554
555 return true;
556 }
557}
558
564{
567
570
571 cDrmBuffer *drmBuffer = nullptr;
574
576 drmBuffer = m_drmBufferQueue.Pop();
577 }
578
580
581 bool pageFlipDone = false;
582 if (drmBuffer) {
584 // check if playback shall start
585 if (PtsToMs(drmBuffer->frame->pts) < m_schedulePlaybackStartAtPtsMs) {
586 drmBuffer->PresentationFinished();
587 return true;
588 } else {
590 m_videoPlaybackPaused = false;
591 }
592 } else if (!m_displayOneFrameThenPause) {
593 // A/V sync
594 int64_t audioPtsMs = m_pAudio->GetHardwareOutputPtsMs();
595 int64_t videoPtsMs = PtsToMs(drmBuffer->frame->pts);
596
597 if (audioPtsMs != AV_NOPTS_VALUE) {
598 int audioBehindVideoByMs = videoPtsMs - audioPtsMs - m_pDevice->GetVideoAudioDelayMs();
599
600 if (m_resumeAudioScheduled && audioBehindVideoByMs >= 0) { // resume audio from pause
601 LOGDEBUG2(L_AV_SYNC, "videorender: resuming audio playback: video %s, audio %s", Timestamp2String(videoPtsMs, 1), Timestamp2String(audioPtsMs, 1));
602 m_pAudio->SetPaused(false);
604 } else if (!m_pAudio->IsPaused() && audioBehindVideoByMs > AV_SYNC_THRESHOLD_AUDIO_BEHIND_VIDEO_MS) { // duplicate frame
605 LogDroppedDuped(audioPtsMs, videoPtsMs, audioBehindVideoByMs);
606
607 m_framePresentationCounter++; // display the current video frame one period longer
608 } else if (!m_pAudio->IsPaused() && !m_lastFrameWasDropped && audioBehindVideoByMs < -AV_SYNC_THRESHOLD_AUDIO_AHEAD_VIDEO_MS) { // drop frame
609 // Drop max every second frame. Otherwise, the buffer gets drained immediately, if multiple frames in a row are dropped.
610 LogDroppedDuped(audioPtsMs, videoPtsMs, audioBehindVideoByMs);
611
612 if (pipBuf)
613 pipBuf->PresentationFinished();
614
615 drmBuffer->PresentationFinished();
616 m_framePresentationCounter--; // skip this pageflip
618
619 return true;
620 }
621
623 }
624
626 m_pAudio->DropSamplesOlderThanPtsMs(drmBuffer->frame->pts * 1000 * av_q2d(m_timebase));
627 }
628
632 }
633
634 pageFlipDone = PageFlip(drmBuffer, pipBuf);
635
638
639 m_lastFrameWasDropped = false;
640 m_pCurrentlyDisplayed = drmBuffer;
642 // display the current frame again in trick speed mode or for A/V syncing.
643 pageFlipDone = PageFlip(m_pCurrentlyDisplayed, pipBuf);
644 } else if ((m_pBufOsd && m_pBufOsd->IsDirty()) || pipBuf) {
645 pageFlipDone = PageFlip(NULL, pipBuf);
646 } else if (m_startgrab) {
647 pageFlipDone = PageFlip(&m_bufBlack, NULL);
648 }
649
650 if (pipBuf) {
653
655 }
656
658
659 return pageFlipDone;
660}
661
663{
664 LOGDEBUG2(L_DRM, "videorender: %s: closing, set a black FB", __FUNCTION__);
665
666 PageFlip(&m_bufBlack, NULL);
667
669 av_frame_free(&m_pCurrentlyDisplayed->frame);
671 m_pCurrentlyDisplayed = nullptr;
672 }
673}
674
675int64_t cVideoRender::PtsToMs(int64_t pts)
676{
677 m_timebaseMutex.Lock();
678 int64_t videoPtsMs = pts * 1000 * av_q2d(m_timebase);
679 m_timebaseMutex.Unlock();
680
681 return videoPtsMs;
682}
683
688{
689 return m_pDrmDevice->HandleEvent();
690}
691
692/*****************************************************************************
693 * OSD
694 ****************************************************************************/
695
700{
701#ifdef USE_GLES
702 if (m_disableOglOsd) {
703 memset((void *)m_pBufOsd->Plane(0), 0,
704 (size_t)(m_pBufOsd->Pitch(0) * m_pBufOsd->Height()));
705 } else {
706 cDrmBuffer *buf;
707
708 EGL_CHECK(eglSwapBuffers(m_pDrmDevice->EglDisplay(), m_pDrmDevice->EglSurface()));
709 m_pNextBo = gbm_surface_lock_front_buffer(m_pDrmDevice->GbmSurface());
710 assert(m_pNextBo);
711
712 buf = m_pDrmDevice->GetBufFromBo(m_pNextBo);
713 if (!buf) {
714 LOGERROR("videorender: %s: Failed to get GL buffer", __FUNCTION__);
715 return;
716 }
717
718 m_pBufOsd = buf;
719
720 // release old buffer for writing again
721 if (m_bo)
722 gbm_surface_release_buffer(m_pDrmDevice->GbmSurface(), m_bo);
723
724 // rotate bos and create and keep bo as m_pOldBo to make it free'able
725 m_pOldBo = m_bo;
726 m_bo = m_pNextBo;
727
728 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));
729 }
730#else
731 memset((void *)m_pBufOsd->Plane(0), 0,
732 (size_t)(m_pBufOsd->Pitch(0) * m_pBufOsd->Height()));
733#endif
734
736 m_osdShown = false;
737}
738
739#define MIN(a, b) ((a) < (b) ? (a) : (b))
740
753void cVideoRender::OsdDrawARGB(int xi, int yi,
754 int width, int height, int pitch,
755 const uint8_t * argb, int x, int y)
756{
757#ifdef USE_GLES
758 if (m_disableOglOsd) {
759 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__,
760 width, height, pitch, argb, x, y, m_pBufOsd->Pitch(0), xi, yi);
761 for (int i = 0; i < height; ++i) {
762 memcpy(m_pBufOsd->Plane(0) + x * 4 + (i + y) * m_pBufOsd->Pitch(0),
763 argb + i * pitch, MIN((size_t)pitch, m_pBufOsd->Pitch(0)));
764 }
765 } else {
766 cDrmBuffer *buf;
767
768 EGL_CHECK(eglSwapBuffers(m_pDrmDevice->EglDisplay(), m_pDrmDevice->EglSurface()));
769 m_pNextBo = gbm_surface_lock_front_buffer(m_pDrmDevice->GbmSurface());
770 assert(m_pNextBo);
771
772 buf = m_pDrmDevice->GetBufFromBo(m_pNextBo);
773 if (!buf) {
774 LOGERROR("videorender: %s: Failed to get GL buffer", __FUNCTION__);
775 return;
776 }
777
778 m_pBufOsd = buf;
779
780 // release old buffer for writing again
781 if (m_bo)
782 gbm_surface_release_buffer(m_pDrmDevice->GbmSurface(), m_bo);
783
784 // rotate bos and create and keep bo as m_pOldBo to make it free'able
785 m_pOldBo = m_bo;
786 m_bo = m_pNextBo;
787
788 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));
789 }
790#else
791 // suppress unused variable warnings ...
792 (void) xi;
793 (void) yi;
794 (void) width;
795
796 for (int i = 0; i < height; ++i) {
797 memcpy(m_pBufOsd->Plane(0) + x * 4 + (i + y) * m_pBufOsd->Pitch(0),
798 argb + i * pitch, (size_t)pitch);
799 }
800#endif
802 m_osdShown = true;
803}
804
805/*****************************************************************************
806 * Thread
807 ****************************************************************************/
808
813{
814 LOGDEBUG("videorender: %s", __FUNCTION__);
815
816 Reset();
817 if (m_pDisplayThread->Active())
819}
820
824static void ReleaseFrame( __attribute__ ((unused)) void *opaque, uint8_t *data)
825{
826 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)data;
827
828 av_free(primedata);
829}
830
837{
838 return m_drmBufferQueue.IsFull();
839}
840
841/*****************************************************************************
842 * Buffer reuse strategy: use-once
843 ****************************************************************************/
845{
846 cDrmBuffer *buf = pool->FindUninitilized();
847
848 if (buf)
849 buf->SetDestroyAfterUse(true);
850
851 return buf;
852}
853
854/*****************************************************************************
855 * Buffer reuse strategy: reuse
856 ****************************************************************************/
858{
859 cDrmBuffer *buf = pool->FindByDmaBufHandle(primedata->objects[0].fd);
860
861 if (buf)
862 return buf;
863 else
864 return pool->FindUninitilized();
865}
866
868{
870
871 if (buf)
872 return buf;
873 else
874 return pool->FindUninitilized();
875}
876
877/*****************************************************************************
878 * Decoding strategy: software
879 ****************************************************************************/
880AVFrame *cDecodingStrategySoftware::PrepareDrmBuffer(cDrmBuffer *buf, int drmDeviceFd, AVFrame *inframe)
881{
882 if (!buf->IsDirty()) {
883 buf->Setup(drmDeviceFd, inframe->width, inframe->height, DRM_FORMAT_NV12, nullptr, true);
884
885 int dmaBufHandle;
886 if (drmPrimeHandleToFD(drmDeviceFd, buf->PrimeHandle(0), DRM_CLOEXEC | DRM_RDWR, &dmaBufHandle))
887 LOGFATAL("videorender: %s: Failed to retrieve the Prime FD (%d): %m", __FUNCTION__, errno);
888
889 buf->SetDmaBufHandle(dmaBufHandle);
890 }
891
892 for (int i = 0; i < inframe->height; ++i)
893 memcpy(buf->Plane(0) + i * buf->Pitch(0), inframe->data[0] + i * inframe->linesize[0], inframe->linesize[0]);
894
895 for (int i = 0; i < inframe->height / 2; ++i)
896 memcpy(buf->Plane(1) + i * buf->Pitch(1), inframe->data[1] + i * inframe->linesize[1], inframe->linesize[1]);
897
898 AVFrame *frame = av_frame_alloc();
899 frame->pts = inframe->pts;
900 frame->width = inframe->width;
901 frame->height = inframe->height;
902 frame->format = AV_PIX_FMT_DRM_PRIME;
903 frame->sample_aspect_ratio = inframe->sample_aspect_ratio;
904
905 frame->format = AV_PIX_FMT_DRM_PRIME;
906 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)av_mallocz(sizeof(AVDRMFrameDescriptor));
907 primedata->objects[0].fd = buf->DmaBufHandle();
908 frame->data[0] = (uint8_t *)primedata;
909 frame->buf[0] = av_buffer_create((uint8_t *)primedata, sizeof(*primedata), ReleaseFrame, NULL, AV_BUFFER_FLAG_READONLY);
910
911 av_frame_free(&inframe);
912
913 return frame;
914}
915
916/*****************************************************************************
917 * Decoding strategy: hardware
918 ****************************************************************************/
919AVFrame *cDecodingStrategyHardware::PrepareDrmBuffer(cDrmBuffer *buf, int drmDeviceFd, AVFrame *frame)
920{
921 if (!buf->IsDirty()) {
922 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)frame->data[0];
923 buf->Setup(drmDeviceFd, frame->width, frame->height, 0, primedata, false);
924 }
925
926 return frame;
927}
928
929void cVideoRender::PushMainFrame(AVFrame *frame)
930{
932}
933
934void cVideoRender::PushPipFrame(AVFrame *frame)
935{
937}
938
943 AVFrame *frame,
944 bool trickspeed,
945 std::atomic<cBufferStrategy*> &bufferReuseStrategy,
946 std::atomic<cDecodingStrategy*> &decodingStrategy,
947 cQueue<cDrmBuffer>* drmBufferQueue,
948 cDrmBufferPool *drmBufferPool)
949{
950 if (bufferReuseStrategy == nullptr) {
951 if (trickspeed)
952 bufferReuseStrategy = new cBufferStrategyUseOnce();
953 else if (frame->format == AV_PIX_FMT_DRM_PRIME)
954 bufferReuseStrategy = new cBufferStrategyReuseHardware();
955 else
956 bufferReuseStrategy = new cBufferStrategyReuseSoftware();
957 }
958
959 if (decodingStrategy == nullptr) {
960 if (frame->format == AV_PIX_FMT_DRM_PRIME)
961 decodingStrategy = new cDecodingStrategyHardware();
962 else
963 decodingStrategy = new cDecodingStrategySoftware();
964 }
965
966 // 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.
967 if (m_pts == AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE)
968 m_pts = frame->pts;
969
970 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)frame->data[0];
971 cDrmBuffer *buf = bufferReuseStrategy.load()->GetBuffer(drmBufferPool, primedata);
972
973 if (!buf)
974 LOGFATAL("videorender: %s: no free DRM buffer found. This is a bug.", __FUNCTION__);
975
976 frame = decodingStrategy.load()->PrepareDrmBuffer(buf, m_pDrmDevice->Fd(), frame);
977
978 buf->frame = frame;
979 buf->SetPresentationPending(true);
980
981 drmBufferQueue->Push(buf);
982}
983
992{
993 if (m_pts == AV_NOPTS_VALUE)
994 return AV_NOPTS_VALUE;
995
996 m_timebaseMutex.Lock();
997 int64_t pts = m_pts * 1000 * av_q2d(m_timebase);
998 m_timebaseMutex.Unlock();
999
1000 return pts;
1001}
1002
1009{
1010 m_videoClockMutex.Lock();
1011 m_pts = pts;
1012 m_videoClockMutex.Unlock();
1013}
1014
1021{
1022 int64_t pts;
1023 m_videoClockMutex.Lock();
1024 pts = m_pts;
1025 m_videoClockMutex.Unlock();
1026 return pts;
1027}
1028
1033{
1034 m_startCounter = 0;
1035 LOGDEBUG("videorender: %s: reset m_startCounter %d TrickSpeed %d", __FUNCTION__, m_startCounter, GetTrickSpeed());
1036}
1037
1039{
1040 m_startCounter = 0;
1041 m_framesDuped = 0;
1042 m_framesDropped = 0;
1045
1046 delete m_decodingStrategy;
1047 m_decodingStrategy = nullptr;
1048}
1049
1056void cVideoRender::SetTrickSpeed(int speed, int forward)
1057{
1058 LOGDEBUG2(L_TRICK, "videorender: %s: set trick speed %d %s", __FUNCTION__, speed, forward ? "forward" : "backward");
1059 m_trickspeedMutex.Lock();
1060 m_framePresentationCounter = std::max(1, speed); // speed is 0 in normal playback. Set it to 1 to display the frames exactly once.
1061 m_trickSpeed = speed;
1062 m_trickForward = forward;
1063 m_trickspeedMutex.Unlock();
1064}
1065
1072{
1073 int speed;
1074 m_trickspeedMutex.Lock();
1075 speed = m_trickSpeed * (m_pDevice->VideoStream()->IsInterlaced() ? 2 : 1);
1076 m_trickspeedMutex.Unlock();
1077 return speed;
1078}
1079
1087{
1088 int dir;
1089 m_trickspeedMutex.Lock();
1090 dir = m_trickForward;
1091 m_trickspeedMutex.Unlock();
1092 return dir;
1093}
1094
1095/*****************************************************************************
1096 * Grabbing
1097 ****************************************************************************/
1098
1106{
1107 int timeout = 50;
1108 cMutex mutex;
1109 mutex.Lock();
1110 m_startgrab = true;
1111 int err = 0;
1112
1113 if (!m_grabCond.TimedWait(mutex, timeout)) {
1114 LOGWARNING("videorender: %s: timed out after %dms", __FUNCTION__, timeout);
1115 err = 1;
1116 }
1117
1118 m_startgrab = false;
1119 return err;
1120}
1121
1126{
1127 int size = 0;
1128 cSoftHdGrab *grab = &m_grabVideo;
1129 cDrmBuffer *buf = grab->GetBuf();
1130
1131 // early return if buf = NULL
1132 if (!buf) {
1133 grab->SetData(NULL);
1134 grab->SetSize(0);
1135 return;
1136 }
1137
1138 for (int plane = 0; plane < buf->NumPlanes(); plane++) {
1139 LOGDEBUG2(L_GRAB, "videorender: %s: VIDEO plane %d address %p pitch %d offset %d handle %d size %d", __FUNCTION__,
1140 plane, buf->Plane(plane), buf->Pitch(plane), buf->Offset(plane), buf->PrimeHandle(plane), buf->Size(plane));
1141 }
1142 // result's width and height are original dimensions how buffer is presented on the screen
1143 uint8_t * result = BufToRgb(buf, &size, grab->GetWidth(), grab->GetHeight(), AV_PIX_FMT_RGB24);
1144 grab->SetData(result);
1145 grab->SetSize(size);
1146 grab->FreeBuf();
1147
1148 return;
1149}
1150
1155{
1156 int size = 0;
1157 cSoftHdGrab *grab = &m_grabPip;
1158 cDrmBuffer *buf = grab->GetBuf();
1159
1160 // early return if buf = NULL
1161 if (!buf) {
1162 grab->SetData(NULL);
1163 grab->SetSize(0);
1164 return;
1165 }
1166
1167 for (int plane = 0; plane < buf->NumPlanes(); plane++) {
1168 LOGDEBUG2(L_GRAB, "videorender: %s: PIP plane %d address %p pitch %d offset %d handle %d size %d", __FUNCTION__,
1169 plane, buf->Plane(plane), buf->Pitch(plane), buf->Offset(plane), buf->PrimeHandle(plane), buf->Size(plane));
1170 }
1171 // result's width and height are original dimensions how buffer is presented on the screen
1172 uint8_t * result = BufToRgb(buf, &size, grab->GetWidth(), grab->GetHeight(), AV_PIX_FMT_RGB24);
1173 grab->SetData(result);
1174 grab->SetSize(size);
1175 grab->FreeBuf();
1176
1177 return;
1178}
1179
1180
1185{
1186 int size;
1187 cSoftHdGrab *grab = &m_grabOsd;
1188 cDrmBuffer *buf = grab->GetBuf();
1189
1190 // early return if buf = NULL
1191 if (!buf) {
1192 grab->SetData(NULL);
1193 grab->SetSize(0);
1194 return;
1195 }
1196
1197 for (int plane = 0; plane < buf->NumPlanes(); plane++) {
1198 LOGDEBUG2(L_GRAB, "videorender: %s: OSD plane %d address %p pitch %d offset %d handle %d size %d", __FUNCTION__,
1199 plane, buf->Plane(plane), buf->Pitch(plane), buf->Offset(plane), buf->PrimeHandle(plane), buf->Size(plane));
1200 }
1201 // result's width and height are original dimensions how buffer is presented on the screen
1202 uint8_t * result = BufToRgb(buf, &size, grab->GetWidth(), grab->GetHeight(), AV_PIX_FMT_BGRA);
1203 grab->SetData(result);
1204 grab->SetSize(size);
1205 grab->FreeBuf();
1206
1207 return;
1208}
1209
1214{
1215 if (m_grabOsd.GetBuf())
1217 if (m_grabVideo.GetBuf())
1219 if (m_grabPip.GetBuf())
1221}
1222
1233cSoftHdGrab *cVideoRender::GetGrab(int *size, int *width, int *height, int *x, int *y, int type)
1234{
1235 cSoftHdGrab *grab;
1236 switch (type) {
1237 case 0:
1238 grab = &m_grabVideo;
1239 break;
1240 case 1:
1241 grab = &m_grabOsd;
1242 break;
1243 case 2:
1244 grab = &m_grabPip;
1245 break;
1246 default:
1247 LOGERROR("videorender: %s: unknown grab requested", __FUNCTION__);
1248 return NULL;
1249 }
1250
1251 LOGDEBUG2(L_GRAB, "videorender: %s: %s size %d %dx%d at %d|%d %p", __FUNCTION__, type == 0 ? "VIDEO" : (type == 1 ? "OSD" : "PIP"),
1252 grab->GetSize(), grab->GetWidth(), grab->GetHeight(), grab->GetX(), grab->GetY(), grab->GetData());
1253
1254 if (size)
1255 *size = grab->GetSize();
1256 if (width)
1257 *width = grab->GetWidth();
1258 if (height)
1259 *height = grab->GetHeight();
1260 if (x)
1261 *x = grab->GetX();
1262 if (y)
1263 *y = grab->GetY();
1264
1265 return grab;
1266}
1267
1275void cVideoRender::GetStats(int *duped, int *dropped, int *counter)
1276{
1277 *duped = m_framesDuped;
1278 *dropped = m_framesDropped;
1279 *counter = m_startCounter;
1280}
1281
1282/*****************************************************************************
1283 * Setup and initialization
1284 ****************************************************************************/
1285
1293void cVideoRender::SetScreenSize(int width, int height, uint32_t refreshRate)
1294{
1295 m_pDevice->SetScreenSize(width, height, refreshRate);
1296}
1297
1302{
1303 m_pDisplayThread = new cDisplayThread(this);
1304
1305 if (m_pDrmDevice->Init())
1306 LOGFATAL("videorender: %s: failed", __FUNCTION__);
1307
1308 cDrmPlane *videoPlane = m_pDrmDevice->VideoPlane();
1309 cDrmPlane *osdPlane = m_pDrmDevice->OsdPlane();
1310
1311 // osd FB
1312#ifndef USE_GLES
1313 if (!m_pBufOsd)
1314 m_pBufOsd = new cDrmBuffer();
1315
1316 m_pBufOsd->Setup(m_pDrmDevice->Fd(), m_pDrmDevice->DisplayWidth(), m_pDrmDevice->DisplayHeight(), DRM_FORMAT_ARGB8888, NULL, false);
1317#else
1318 if (m_disableOglOsd) {
1319 if (!m_pBufOsd)
1320 m_pBufOsd = new cDrmBuffer();
1321
1322 m_pBufOsd->Setup(m_pDrmDevice->Fd(), m_pDrmDevice->DisplayWidth(), m_pDrmDevice->DisplayHeight(), DRM_FORMAT_ARGB8888, NULL, false);
1323 }
1324#endif
1325
1326 // black fb
1327 LOGDEBUG2(L_DRM, "videorender: %s: Try to create a black FB", __FUNCTION__);
1328 m_bufBlack.Setup(m_pDrmDevice->Fd(), m_pDrmDevice->DisplayWidth(), m_pDrmDevice->DisplayHeight(), DRM_FORMAT_NV12, NULL, false);
1330
1331 // save actual modesetting
1333
1334 drmModeAtomicReqPtr modeReq;
1335 const uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
1336 uint32_t modeID = 0;
1337
1338 if (m_pDrmDevice->CreatePropertyBlob(&modeID) != 0)
1339 LOGFATAL("videorender: %s: Failed to create mode property blob.", __FUNCTION__);
1340 if (!(modeReq = drmModeAtomicAlloc()))
1341 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
1342
1344 DRM_MODE_OBJECT_CRTC, "MODE_ID", modeID);
1346 DRM_MODE_OBJECT_CONNECTOR, "CRTC_ID", m_pDrmDevice->CrtcId());
1348 DRM_MODE_OBJECT_CRTC, "ACTIVE", 1);
1349
1350 // Osd plane
1351 // 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,
1352 // but initially move the OSD behind the VIDEO
1353#ifndef USE_GLES
1354 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
1356 0, 0, m_pBufOsd->Width(), m_pBufOsd->Height());
1357
1358 osdPlane->SetPlane(modeReq);
1359#else
1360 if (m_disableOglOsd) {
1361 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
1363 0, 0, m_pBufOsd->Width(), m_pBufOsd->Height());
1364
1365 osdPlane->SetPlane(modeReq);
1366 }
1367#endif
1368 if (m_pDrmDevice->UseZpos()) {
1369 videoPlane->SetZpos(m_pDrmDevice->ZposOverlay());
1370 videoPlane->SetPlaneZpos(modeReq);
1371#ifdef USE_GLES
1372 osdPlane->SetZpos(m_pDrmDevice->ZposPrimary());
1373 osdPlane->SetPlaneZpos(modeReq);
1374#endif
1375 }
1376
1377 // Black buffer for video plane
1378 videoPlane->SetParams(m_pDrmDevice->CrtcId(), m_bufBlack.Id(),
1380 0, 0, m_bufBlack.Width(), m_bufBlack.Height());
1381
1382 videoPlane->SetPlane(modeReq);
1383
1384 if (drmModeAtomicCommit(m_pDrmDevice->Fd(), modeReq, flags, NULL) != 0) {
1385#ifndef USE_GLES
1386 osdPlane->DumpParameters("osd");
1387#endif
1388 videoPlane->DumpParameters("video");
1389
1390 drmModeAtomicFree(modeReq);
1391 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
1392 }
1393
1394 drmModeAtomicFree(modeReq);
1395
1396 m_osdShown = false;
1397
1398 // init variables page flip
1400}
1401
1406{
1407 cDrmPlane *videoPlane = m_pDrmDevice->VideoPlane();
1408 cDrmPlane *osdPlane = m_pDrmDevice->OsdPlane();
1409
1411
1412 // restore saved CRTC configuration
1414
1415 videoPlane->FreeProperties();
1416 osdPlane->FreeProperties();
1417
1419#ifdef USE_GLES
1420 if (m_disableOglOsd) {
1421 if (m_pBufOsd) {
1422 m_pBufOsd->Destroy();
1423 delete m_pBufOsd;
1424 }
1425 } else {
1426 if (m_pNextBo)
1427 gbm_bo_destroy(m_pNextBo);
1428 if (m_pOldBo)
1429 gbm_bo_destroy(m_pOldBo);
1430 }
1431#else
1432 if (m_pBufOsd) {
1433 m_pBufOsd->Destroy();
1434 delete m_pBufOsd;
1435 }
1436#endif
1437
1439}
1440
1447{
1448 m_videoRect.Set(rect.Point(), rect.Size());
1449
1450 if (m_videoRect.IsEmpty())
1451 m_videoIsScaled = false;
1452 else
1453 m_videoIsScaled = true;
1454
1455 LOGDEBUG("videorender: %s: %d %d %d %d%s", __FUNCTION__, rect.X(), rect.Y(), rect.Width(), rect.Height(), m_videoIsScaled ? ", video is scaled" : "");
1456}
1457
1462{
1463 for (Event event : m_eventQueue)
1465
1466 m_eventQueue.clear();
1467}
1468
1470{
1471 if (useAlt) {
1475 } else {
1479 }
1480}
Audio and alsa module header file.
uint8_t * BufToRgb(cDrmBuffer *buf, int *size, int dstW, int dstH, enum AVPixelFormat dstPixFmt)
Convert a DRM buffer to rgb format image.
Definition: buf2rgb.cpp:74
Some helper functions header file.
virtual void OnEventReceived(const Event &)=0
cDrmBuffer * GetBuffer(cDrmBufferPool *, AVDRMFrameDescriptor *) override
cDrmBuffer * GetBuffer(cDrmBufferPool *, AVDRMFrameDescriptor *) override
cDrmBuffer * GetBuffer(cDrmBufferPool *, AVDRMFrameDescriptor *) override
AVFrame * PrepareDrmBuffer(cDrmBuffer *, int, AVFrame *) override
AVFrame * PrepareDrmBuffer(cDrmBuffer *, int, AVFrame *) override
Display thread class.
Definition: threads.h:61
void Stop(void)
Definition: threads.cpp:117
cDrmBuffer * FindUninitilized(void)
Definition: drmbuffer.cpp:423
cDrmBuffer * FindByDmaBufHandle(int)
Definition: drmbuffer.cpp:413
cDrmBuffer * FindNoPresentationPending(void)
Definition: drmbuffer.cpp:436
void DestroyAllExcept(cDrmBuffer *)
Definition: drmbuffer.cpp:446
void MarkClean(void)
Definition: drmbuffer.h:75
uint32_t Pitch(int idx)
Definition: drmbuffer.h:96
int DmaBufHandle(void)
Definition: drmbuffer.h:85
void Setup(int, uint32_t, uint32_t, uint32_t, AVDRMFrameDescriptor *, bool)
Setup the buffer.
Definition: drmbuffer.cpp:270
uint32_t Width(void)
Definition: drmbuffer.h:67
uint32_t Height(void)
Definition: drmbuffer.h:69
uint8_t * Plane(int idx)
Definition: drmbuffer.h:89
int NumPlanes(void)
Definition: drmbuffer.h:83
void MarkDirty(void)
Definition: drmbuffer.h:76
uint32_t Size(int idx)
Definition: drmbuffer.h:99
void FillBlack(void)
Color the buffer black.
Definition: drmbuffer.cpp:404
uint32_t PrimeHandle(int idx)
Definition: drmbuffer.h:90
AVFrame * frame
associated AVFrame
Definition: drmbuffer.h:102
int Id(void)
Definition: drmbuffer.h:80
bool IsDirty(void)
Definition: drmbuffer.h:74
uint32_t Offset(int idx)
Definition: drmbuffer.h:93
void PresentationFinished(void)
Definition: drmbuffer.cpp:456
void Destroy(void)
Clear and destroy the buffer object and its parameters.
Definition: drmbuffer.cpp:164
void SetDmaBufHandle(uint32_t fd)
Definition: drmbuffer.h:86
void SetDestroyAfterUse(bool val)
Definition: drmbuffer.h:103
int CreatePropertyBlob(uint32_t *)
Creates a property blob.
Definition: drmdevice.cpp:951
int Fd(void)
Definition: drmdevice.h:82
int SetPropertyRequest(drmModeAtomicReqPtr, uint32_t, uint32_t, const char *, uint64_t)
Add a property to a request.
Definition: drmdevice.cpp:959
uint64_t DisplayHeight(void)
Definition: drmdevice.h:89
int HandleEvent(void)
Polls for a drm event.
Definition: drmdevice.cpp:1013
uint64_t DisplayWidth(void)
Definition: drmdevice.h:88
uint64_t ZposPrimary(void)
Definition: drmdevice.h:94
uint64_t ZposOverlay(void)
Definition: drmdevice.h:93
cDrmPlane * PipPlane(void)
Definition: drmdevice.h:98
void SaveCrtc(void)
Saves information of a CRTC.
Definition: drmdevice.cpp:993
uint32_t CrtcId(void)
Definition: drmdevice.h:91
void RestoreCrtc(void)
Restore information of a CRTC.
Definition: drmdevice.cpp:1001
void Close(void)
Close drm file handle.
Definition: drmdevice.cpp:939
cDrmPlane * OsdPlane(void)
Definition: drmdevice.h:96
cDrmPlane * VideoPlane(void)
Definition: drmdevice.h:97
uint32_t ConnectorId(void)
Definition: drmdevice.h:86
int Init(void)
Initiate the drm device.
Definition: drmdevice.cpp:258
void InitEvent(void)
Init the event context.
Definition: drmdevice.cpp:1021
int UseZpos(void)
Definition: drmdevice.h:92
cDrmPlane - DRM plane class
Definition: drmplane.h:30
void SetParams(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t)
Set the modesetting parameters of a plane.
Definition: drmplane.cpp:104
void ClearPlane(drmModeAtomicReqPtr)
Clear plane from drm.
Definition: drmplane.cpp:183
uint64_t GetZpos(void)
Definition: drmplane.h:60
void SetZpos(uint64_t zpos)
Definition: drmplane.h:61
uint64_t GetFbId(void)
Definition: drmplane.h:51
void FreeProperties(void)
Free the previously filled plane properties.
Definition: drmplane.cpp:71
void SetPlane(drmModeAtomicReqPtr)
Set all other plane properties.
Definition: drmplane.cpp:162
void SetPlaneZpos(drmModeAtomicReqPtr)
Set the plane zpos property.
Definition: drmplane.cpp:152
uint32_t GetId(void)
Definition: drmplane.h:46
void DumpParameters(const char *)
Dump the plane parameter modesetting values.
Definition: drmplane.cpp:223
void Clear(void)
Remove all elements from the queue.
Definition: queue.h:95
T * Pop(void)
Pop an element from the back of the queue.
Definition: queue.h:64
bool IsEmpty(void)
Check if the queue is empty.
Definition: queue.h:106
bool IsFull(void)
Check if the queue is full.
Definition: queue.h:117
bool Push(T *element)
Push an element to the front of the queue.
Definition: queue.h:47
size_t Size(void)
Get the current size of the queue.
Definition: queue.h:128
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
Definition: audio.cpp:884
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition: audio.cpp:599
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
Definition: audio.cpp:845
void SetPaused(bool)
Set audio playback paused state.
Definition: audio.cpp:944
bool IsPaused(void)
Definition: audio.h:54
const char * ConfigDisplayResolution
display resolution (syntax: "1920x1080@50")
Definition: config.h:78
int ConfigPipAltTopPercent
0 = aligned to top, 100 = aligned to bottom
Definition: config.h:72
int ConfigPipLeftPercent
0 = aligned to left, 100 = aligned to right
Definition: config.h:65
int ConfigPipAltLeftPercent
0 = aligned to left, 100 = aligned to right
Definition: config.h:71
int ConfigPipAltScalePercent
alternative scale factor of pip video
Definition: config.h:70
int ConfigPipTopPercent
0 = aligned to top, 100 = aligned to bottom
Definition: config.h:66
int ConfigPipScalePercent
scale factor of pip video
Definition: config.h:64
int ConfigPipUseAlt
Definition: config.h:68
int GetVideoAudioDelayMs(void)
Definition: softhddevice.h:195
bool IsBufferingThresholdReached(void)
Check if the buffering threshold has been reached.
cVideoStream * VideoStream(void)
Definition: softhddevice.h:175
cSoftHdConfig * Config(void)
Definition: softhddevice.h:174
void SetScreenSize(int, int, uint32_t)
Set the screen size.
cSoftHdAudio * Audio(void)
Definition: softhddevice.h:177
cSoftHdGrab - Grabber class
Definition: grab.h:35
void SetBuf(cDrmBuffer *buf)
Definition: grab.h:45
int GetWidth(void)
Definition: grab.h:49
uint8_t * GetData(void)
Definition: grab.h:51
cDrmBuffer * GetBuf(void)
Definition: grab.h:53
void SetRect(int x, int y, int width, int height)
Definition: grab.h:42
int GetY(void)
Definition: grab.h:48
int GetHeight(void)
Definition: grab.h:50
void SetSize(int size)
Definition: grab.h:44
int GetSize(void)
Definition: grab.h:52
void FreeBuf(void)
Free the grab buffer.
Definition: grab.cpp:52
void SetData(uint8_t *result)
Definition: grab.h:43
int GetX(void)
Definition: grab.h:47
void PushPipFrame(AVFrame *)
void SetFrameFlags(AVFrame *, int)
Set frame flags.
int m_numWrongProgressive
counter for progressive frames sent in an interlaced stream (only used for logging)
Definition: videorender.h:213
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
Definition: videorender.h:225
void SetScreenSize(int, int, uint32_t)
Wrapper to set the screen size in the device.
bool m_osdShown
set, if osd is shown currently
Definition: videorender.h:243
cCondVar m_grabCond
condition gets signalled, if renederer finished to clone the grabbed buffers
Definition: videorender.h:217
cDrmBuffer * m_pCurrentlyDisplayed
pointer to currently displayed DRM buffer
Definition: videorender.h:241
int m_pipScalePercent
scale factor for pip
Definition: videorender.h:234
void ConvertPipBufToRgb(void)
Convert the pip drm buffer to an rgb image.
std::atomic< bool > m_resumeAudioScheduled
set, if audio resume is scheduled after a pause
Definition: videorender.h:245
void ConvertVideoBufToRgb(void)
Convert the video drm buffer to an rgb image.
std::atomic< bool > m_displayOneFrameThenPause
set, if only one frame shall be displayed and then pause playback
Definition: videorender.h:246
std::vector< Event > m_eventQueue
event queue for incoming events
Definition: videorender.h:206
cDrmBufferPool m_pipDrmBufferPool
PIP pool of drm buffers.
Definition: videorender.h:251
void ProcessEvents(void)
Process queued events and forward to event receiver.
int m_framesDropped
number of frames dropped
Definition: videorender.h:226
void OsdClear(void)
Clear the OSD (draw an empty/ transparent OSD)
cDisplayThread * m_pDisplayThread
pointer to display thread
Definition: videorender.h:203
int64_t m_pts
current video PTS
Definition: videorender.h:230
cSoftHdGrab * GetGrab(int *, int *, int *, int *, int *, int)
Get the grabbed image.
int m_trickSpeed
current trick speed
Definition: videorender.h:210
cSoftHdGrab m_grabVideo
keeps the current grabbed video
Definition: videorender.h:219
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)
Definition: videorender.h:244
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.
cMutex m_trickspeedMutex
mutex used while accessing trickspeed parameters
Definition: videorender.h:204
cQueue< cDrmBuffer > m_drmBufferQueue
queue for DRM buffers to be displayed (VIDEO_SURFACES_MAX is defined in thread.h)
Definition: videorender.h:208
cRect m_lastVideoGrab
crtc rect of the last shown video frame
Definition: videorender.h:221
virtual ~cVideoRender(void)
cVideoRender destructor
cRect m_videoRect
rect of the currently displayed video
Definition: videorender.h:232
bool m_startgrab
internal flag to trigger grabbing
Definition: videorender.h:216
void GetStats(int *, int *, int *)
Get some rendering statistics.
std::atomic< cDecodingStrategy * > m_pipDecodingStrategy
strategy for decoding setup
Definition: videorender.h:255
void SetTrickSpeed(int, int)
Set the trickspeed parameters.
std::atomic< cBufferStrategy * > m_bufferReuseStrategy
strategy to select drm buffers
Definition: videorender.h:252
void DisplayBlackFrame(void)
void SetVideoClock(int64_t)
Wrapper to set the video clock (m_pts)
cSoftHdAudio * m_pAudio
pointer to cSoftHdAudio
Definition: videorender.h:201
AVRational m_timebase
timebase used for pts, set by first RenderFrame()
Definition: videorender.h:228
void PushMainFrame(AVFrame *)
void SetVideoOutputPosition(const cRect &)
Set size and position of the video on the screen.
cVideoRender(cSoftHdDevice *)
cVideoRender constructor
Definition: videorender.cpp:80
int GetFrameFlags(AVFrame *)
Get frame flags.
int m_pipTopPercent
top margin for pip
Definition: videorender.h:236
cDrmBuffer * m_pBufOsd
pointer to osd drm buffer object
Definition: videorender.h:239
int64_t GetOutputPtsMs(void)
Get the output PTS in milliseconds.
cSoftHdDevice * m_pDevice
pointer to cSoftHdDevice
Definition: videorender.h:200
int m_framePresentationCounter
number of times the current frame has to be shown (for slow motion)
Definition: videorender.h:212
std::atomic< cBufferStrategy * > m_pipBufferReuseStrategy
strategy to select drm buffers
Definition: videorender.h:253
cSoftHdConfig * m_pConfig
pointer to cSoftHdConfig
Definition: videorender.h:202
void ClearPipDecoderToDisplayQueue(void)
Clear (empty) the decoder to display queue.
void ClearGrab(void)
Clear the grab drm buffers.
bool IsPipActive(void)
Definition: videorender.h:195
cSoftHdGrab m_grabOsd
keeps the current grabbed osd
Definition: videorender.h:218
int CommitBuffer(cDrmBuffer *, cDrmBuffer *)
Commit the frame to the hardware.
int TriggerGrab(void)
Trigger a screen grab.
IEventReceiver * m_pEventReceiver
pointer to event receiver
Definition: videorender.h:249
cQueue< cDrmBuffer > m_pipDrmBufferQueue
queue for PIP DRM buffers to be displayed (VIDEO_SURFACES_MAX is defined in thread....
Definition: videorender.h:209
cRect m_lastPipGrab
crtc rect of the last shown pip frame
Definition: videorender.h:222
void LogDroppedDuped(int64_t, int64_t, int)
Log A/V sync debug message.
void SetPipBuffer(cDrmBuffer *)
Modesetting for pip.
bool DisplayFrame()
Display the frame (video and/or osd)
void ConvertOsdBufToRgb(void)
Convert the osd drm buffer to an rgb image.
void SetVideoBuffer(cDrmBuffer *)
Modesetting for video.
cDrmBuffer * m_pCurrentlyPipDisplayed
pointer to currently displayed DRM buffer
Definition: videorender.h:242
int64_t GetVideoClock(void)
Wrapper to get the video clock (m_pts)
int GetTrickForward(void)
Get the current trickspeed direction.
void OsdDrawARGB(int, int, int, int, int, const uint8_t *, int, int)
Draw an OSD ARGB image.
cDrmDevice * m_pDrmDevice
pointer cDrmDevice object
Definition: videorender.h:238
void Grab(cDrmBuffer *, cDrmBuffer *)
Grab video and osd.
cMutex m_timebaseMutex
mutex used around m_timebase
Definition: videorender.h:229
cMutex m_videoClockMutex
mutex used around m_pts
Definition: videorender.h:205
int64_t PtsToMs(int64_t)
std::atomic< cDecodingStrategy * > m_decodingStrategy
strategy for decoding setup
Definition: videorender.h:254
int GetTrickSpeed(void)
Get the current trickspeed.
bool m_lastFrameWasDropped
true, if the last frame was dropped
Definition: videorender.h:227
void SetPipSize(bool)
bool PageFlip(cDrmBuffer *, cDrmBuffer *)
Do the pageflip.
int m_pipLeftPercent
left margin for pip
Definition: videorender.h:235
cDrmBufferPool m_drmBufferPool
pool of drm buffers
Definition: videorender.h:250
std::atomic< int64_t > m_schedulePlaybackStartAtPtsMs
if set, frames with PTS older than this will be dropped
Definition: videorender.h:247
void ResetFrameCounter(void)
Send start condition to video thread.
int m_startCounter
counter for displayed frames, indicates a video start
Definition: videorender.h:224
bool m_trickForward
true, if trickspeed plays forward
Definition: videorender.h:211
bool m_videoIsScaled
true, if the currently displayed video is scaled
Definition: videorender.h:233
void ExitDisplayThread(void)
Stop display thread.
cDrmBuffer m_bufBlack
black drm buffer object
Definition: videorender.h:240
cSoftHdGrab m_grabPip
keeps the current grabbed pip video
Definition: videorender.h:220
bool IsInterlaced(void)
Definition: videostream.h:57
size_t GetAvPacketsFilled(void)
Definition: videostream.h:70
DRM device header file.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent > Event
Definition: event.h:72
@ VIDEO
Definition: event.h:25
#define EGL_CHECK(stmt)
Definition: glhelpers.h:60
Grabber header file.
Logger class header file.
#define LOGDEBUG2
Definition: logger.h:50
#define LOGDEBUG
Definition: logger.h:49
#define LOGERROR
Definition: logger.h:46
#define L_DRM
Definition: logger.h:62
#define L_GRAB
Definition: logger.h:71
#define L_AV_SYNC
Definition: logger.h:59
#define LOGWARNING
Definition: logger.h:47
#define L_PACKET
Definition: logger.h:70
#define L_OPENGL
Definition: logger.h:67
#define LOGFATAL
Logger macros.
Definition: logger.h:45
#define L_TRICK
Definition: logger.h:65
#define L_OSD
Definition: logger.h:61
Misc function header file.
#define AV_NOPTS_VALUE
Definition: misc.h:35
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Workaround for av_err2str() not working with C++.
Definition: misc.h:81
uint64_t y
uint64_t x
uint64_t h
uint64_t w
Thread classes header file.
#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.
Rendering class header file.
#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:76
#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:77