vdr-plugin-softhddevice-drm-gles 1.5.9-20e15de
videorender.cpp
Go to the documentation of this file.
1
25#include <cerrno>
26#include <cinttypes>
27#include <cstdint>
28#include <mutex>
29#include <vector>
30
31#ifdef USE_GLES
32#include <assert.h>
33#include <gbm.h>
34#include <EGL/egl.h>
35#endif
36
37extern "C" {
38#include <libavcodec/avcodec.h>
39#include <libavutil/hwcontext_drm.h>
40}
41
42#include <drm_fourcc.h>
43#include <vdr/osd.h>
44#include <vdr/thread.h>
45#include <xf86drmMode.h>
46
47#include "audio.h"
48#include "config.h"
49#include "drmdevice.h"
50#include "event.h"
51#include "grab.h"
52#include "logger.h"
53#include "misc.h"
54#include "queue.h"
55#include "softhddevice.h"
56#include "threads.h"
57#include "videorender.h"
58#include "videostream.h"
59
60/*****************************************************************************
61 * cVideoRender class
62 ****************************************************************************/
63
70 : m_pDevice(device),
71 m_pAudio(m_pDevice->Audio()),
72 m_pConfig(m_pDevice->Config()),
73 m_pDrmDevice(new cDrmDevice(this, m_pConfig->ConfigDisplayResolution)),
74 m_pEventReceiver(device)
75{
76#ifdef USE_GLES
77 m_disableOglOsd = m_pConfig->ConfigDisableOglOsd;
78 m_bo = nullptr;
79 m_pNextBo = nullptr;
80 m_pOldBo = nullptr;
81#endif
82 m_timebase = av_make_q(1, 90000);
84}
85
90{
91 LOGDEBUG2(L_DRM, "videorender: %s", __FUNCTION__);
92
94 delete m_pDisplayThread;
95
96 delete m_pDrmDevice;
97}
98
110
121
122struct sRect {
123 uint64_t x;
124 uint64_t y;
125 uint64_t w;
126 uint64_t h;
127};
128
140static sRect ComputeFittedRect(AVFrame *frame, uint64_t dispX, uint64_t dispY, uint64_t dispWidth, uint64_t dispHeight)
141{
142 if (!frame || dispWidth == 0 || dispHeight == 0)
143 return { dispX, dispY, dispWidth, dispHeight };
144
145 double frameWidth = frame->width > 0 ? frame->width : 1.0;
146 double frameHeight = frame->height > 0 ? frame->height : 1.0;
147 double frameSar = av_q2d(frame->sample_aspect_ratio) ? av_q2d(frame->sample_aspect_ratio) : 1.0;
148 double dispAspect = static_cast<double>(dispWidth) / static_cast<double>(dispHeight);
149 double frameAspect = frameWidth / frameHeight * frameSar;
150
151 double picWidthD = dispWidth;
152 double picHeightD = dispHeight;
153
154 if (dispAspect > frameAspect) {
155 // letterbox horizontally (frame narrower than display)
156 picWidthD = dispHeight * frameAspect;
157 if (picWidthD <= 0 || picWidthD > dispWidth)
158 picWidthD = dispWidth;
159 } else {
160 // pillarbox vertically (frame wider than display)
161 picHeightD = dispWidth / frameAspect;
162 if (picHeightD <= 0 || picHeightD > dispHeight)
163 picHeightD = dispHeight;
164 }
165
166 // round to the nearest pixel
167 uint64_t picWidth = std::llround(std::max(0.0, picWidthD));
168 uint64_t picHeight = std::llround(std::max(0.0, picHeightD));
169
170 int64_t offsetX = static_cast<int64_t>(dispWidth) - static_cast<int64_t>(picWidth);
171 int64_t offsetY = static_cast<int64_t>(dispHeight) - static_cast<int64_t>(picHeight);
172 uint64_t posX = dispX + static_cast<uint64_t>(std::max<int64_t>(0, offsetX / 2));
173 uint64_t posY = dispY + static_cast<uint64_t>(std::max<int64_t>(0, offsetY / 2));
174
175 return { posX, posY, picWidth, picHeight };
176}
177
187{
188 if (!buf)
189 return 1;
190
191 AVFrame *frame = buf->frame;
192
193 // set display dimensions as default
194 uint64_t dispWidth = m_pDrmDevice->DisplayWidth();
195 uint64_t dispHeight = m_pDrmDevice->DisplayHeight();
196 uint64_t dispX = 0;
197 uint64_t dispY = 0;
198
199 cDrmPlane *videoPlane = m_pDrmDevice->VideoPlane();
200
201 // get video size and position
202 if (m_videoIsScaled) {
203 dispWidth = m_videoRect.Width();
204 dispHeight = m_videoRect.Height();
205 dispX = m_videoRect.X();
206 dispY = m_videoRect.Y();
207 }
208
209 // fit frame into display
210 sRect fittedRect = ComputeFittedRect(frame, dispX, dispY, dispWidth, dispHeight);
211
212 // now set the plane parameters
213 videoPlane->SetParams(m_pDrmDevice->CrtcId(), buf->Id(),
214 fittedRect.x, fittedRect.y, fittedRect.w, fittedRect.h,
215 0, 0, buf->Width(), buf->Height());
216
217 buf->SetSizeOnScreen(fittedRect.x, fittedRect.y, fittedRect.w, fittedRect.h); // remember for grab
218
219 return 0;
220}
221
228int cVideoRender::SetOsdBuffer(drmModeAtomicReqPtr modeReq)
229{
230 if (!m_pBufOsd || !m_pBufOsd->IsDirty())
231 return 1;
232
233 cDrmPlane *videoPlane = m_pDrmDevice->VideoPlane();
234 cDrmPlane *osdPlane = m_pDrmDevice->OsdPlane();
235
236 // We had draw activity on the osd buffer
237 if (m_pDrmDevice->UseZpos()) {
240 videoPlane->SetPlaneZpos(modeReq);
241 osdPlane->SetPlaneZpos(modeReq);
242
243 LOGDEBUG2(L_DRM, "videorender: %s: SetPlaneZpos: video->plane_id %d -> zpos %" PRIu64 ", osd->plane_id %d -> zpos %" PRIu64 "", __FUNCTION__,
244 videoPlane->GetId(), videoPlane->GetZpos(),
245 osdPlane->GetId(), osdPlane->GetZpos());
246 }
247
248 uint64_t crtcW = m_osdShown ? m_pBufOsd->Width() : 0;
249 uint64_t crtcH = m_osdShown ? m_pBufOsd->Height() : 0;
250
251 // now set the plane parameters
252 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
253 0, 0, crtcW, crtcH,
254 0, 0, crtcW, crtcH);
255
256 m_pBufOsd->SetSizeOnScreen(0, 0, crtcW, crtcH); // remember for grab
257
259 return 0;
260}
261
271{
272 if (!buf || !m_pipActive || m_videoIsScaled)
273 return 1;
274
275 AVFrame *frame = buf->frame;
276
277 // set display dimensions as default
278 uint64_t dispWidth = m_pDrmDevice->DisplayWidth();
279 uint64_t dispHeight = m_pDrmDevice->DisplayHeight();
280 uint64_t dispX = 0;
281 uint64_t dispY = 0;
282
283 cDrmPlane *pipPlane = m_pDrmDevice->PipPlane();
284
285 // Get video size and position
286 if (m_videoIsScaled) {
287 dispWidth = m_videoRect.Width();
288 dispHeight = m_videoRect.Height();
289 dispX = m_videoRect.X();
290 dispY = m_videoRect.Y();
291 }
292
293 // fit frame into display
294 sRect fittedRect = ComputeFittedRect(frame, dispX, dispY, dispWidth, dispHeight);
295
296 // compute pip window with given scaling and positioning values from menu
297 int64_t centerOffsetX = static_cast<int64_t>(dispWidth) - static_cast<int64_t>(fittedRect.w);
298 int64_t centerOffsetY = static_cast<int64_t>(dispHeight) - static_cast<int64_t>(fittedRect.h);
299 centerOffsetX = std::max<int64_t>(0, centerOffsetX / 2);
300 centerOffsetY = std::max<int64_t>(0, centerOffsetY / 2);
301
302 double crtcWD = fittedRect.w * m_pipScalePercent / 100.0;
303 double crtcHD = fittedRect.h * m_pipScalePercent / 100.0;
304 uint64_t crtcW = std::llround(crtcWD);
305 uint64_t crtcH = std::llround(crtcHD);
306
307 double spaceW = dispWidth - crtcW - centerOffsetX;
308 double spaceH = dispHeight - crtcH - centerOffsetY;
309
310 uint64_t crtcX = dispX + std::llround(spaceW * m_pipLeftPercent / 100.0 + centerOffsetX * m_pipScalePercent / 100.0);
311 uint64_t crtcY = dispY + std::llround(spaceH * m_pipTopPercent / 100.0 + centerOffsetY * m_pipScalePercent / 100.0);
312
313 // now set the plane parameters
314 pipPlane->SetParams(m_pDrmDevice->CrtcId(), buf->Id(),
315 crtcX, crtcY, crtcW, crtcH,
316 0, 0, buf->Width(), buf->Height());
317
318 buf->SetSizeOnScreen(crtcX, crtcY, crtcW, crtcH); // remember for grab
319
320 return 0;
321}
322
332{
333 enum modeSetLevel {
334 MODESET_OSD = (1 << 0),
335 MODESET_VIDEO = (1 << 1),
336 MODESET_PIP = (1 << 2)
337 };
338
339 int modeSet = 0;
340 int fdDrm = m_pDrmDevice->Fd();
341 cDrmPlane *videoPlane = m_pDrmDevice->VideoPlane();
342 cDrmPlane *osdPlane = m_pDrmDevice->OsdPlane();
343 cDrmPlane *pipPlane = m_pDrmDevice->PipPlane();
344 drmModeAtomicReqPtr modeReq;
345 uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT;
346
347 if (!(modeReq = drmModeAtomicAlloc())) {
348 LOGERROR("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
349 return -1;
350 }
351
352 // handle the video plane
353 // If no new video is available, set the old buffer again, if available.
354 // This is necessary to recognize a size-change in SetVideoBuffer().
355 // Though this is not expensive, maybe we should only call that, if size really changed.
357 videoPlane->SetPlane(modeReq);
358 modeSet |= MODESET_VIDEO;
359// LOGDEBUG2(L_DRM, "videorender: %s: SetPlane Video (fb = %" PRIu64 ")", __FUNCTION__, videoPlane->GetFbId());
360 }
361
362 // handle the pip plane
363 if (pipPlane->GetId()) {
365 pipPlane->SetPlane(modeReq);
366 else
367 pipPlane->ClearPlane(modeReq);
368
369 modeSet |= MODESET_PIP;
370 }
371
372 // handle the osd plane
373 if (!SetOsdBuffer(modeReq)) {
374 osdPlane->SetPlane(modeReq);
375 modeSet |= MODESET_OSD;
376 LOGDEBUG2(L_DRM, "videorender: %s: SetPlane OSD %d (fb = %" PRIu64 ")", __FUNCTION__, m_osdShown, osdPlane->GetFbId());
377 }
378
379 // return without an atomic commit (no video frame and osd activity)
380 if (!modeSet) {
381 drmModeAtomicFree(modeReq);
382 return -1;
383 }
384
385 // do the atomic commit
386 if (drmModeAtomicCommit(fdDrm, modeReq, flags, NULL) != 0) {
387 if (modeSet & MODESET_OSD)
388 osdPlane->DumpParameters("osd");
389 if (modeSet & MODESET_VIDEO)
390 videoPlane->DumpParameters("video");
391 if (modeSet & MODESET_PIP)
392 pipPlane->DumpParameters("pip");
393
394 drmModeAtomicFree(modeReq);
395 LOGERROR("videorender: %s: page flip failed (%d): %m", __FUNCTION__, errno);
396 return -1;
397 }
398
399 drmModeAtomicFree(modeReq);
400
401 return 0;
402}
403
411void cVideoRender::LogDroppedDuped(int64_t audioPtsMs, int64_t videoPtsMs, int audioBehindVideoByMs)
412{
413 LOGDEBUG2(L_AV_SYNC, "Frame %s (drop %d, dup %d) Pkts %d Frames %d UsedBytes %d audio %s video %s Delay %dms diff %dms",
414 audioBehindVideoByMs > 0 ? "duped" : "dropped",
420 Timestamp2String(audioPtsMs, 1),
421 Timestamp2String(videoPtsMs, 1),
423 audioBehindVideoByMs);
424
425 if (audioBehindVideoByMs > 0)
427 else
429
430}
431
440{
441 if (!frame || !frame->opaque_ref)
442 return 0;
443
444 int *frameFlags = (int *)frame->opaque_ref->data;
445 return *frameFlags;
446}
447
454void cVideoRender::SetFrameFlags(AVFrame *frame, int flags)
455{
456 int *frameFlags;
457 if (!frame->opaque_ref) {
458 frame->opaque_ref = av_buffer_allocz(sizeof(*frameFlags));
459 if (!frame->opaque_ref) {
460 LOGFATAL("videorender: %s: cannot allocate private frame data", __FUNCTION__);
461 }
462 }
463
464 frameFlags = (int *)frame->opaque_ref->data;
465 *frameFlags = flags;
466}
467
476{
477 if (CommitBuffer(buf, pipBuf) < 0) {
478 // no modesetting was done
479 if (buf && buf->frame)
480 av_frame_free(&buf->frame);
481 if (pipBuf && pipBuf->frame)
482 av_frame_free(&pipBuf->frame);
483
484 return false;
485 } else {
486 if (m_pDrmDevice->HandleEvent() != 0)
487 LOGERROR("threads: display thread: drmHandleEvent failed!");
488
489 // now, that we had a successful commit, set the STC if we have a frame. Skip if only the OSD was updated.
490 if (buf && buf->frame) {
491 if (buf->frame->pts != AV_NOPTS_VALUE)
492 SetVideoClock(buf->frame->pts);
493
494 LOGDEBUG2(L_PACKET, "videorender: %s: ID %d: PTS %s", __FUNCTION__, buf->Id(), Timestamp2String(buf->frame->pts, 90));
495 }
496
497 return true;
498 }
499}
500
506{
509
512
513 cDrmBuffer *drmBuffer = nullptr;
516 drmBuffer = m_drmBufferQueue.Pop();
517 }
518
520
521 bool pageFlipDone = false;
522 if (drmBuffer) {
525 int64_t interFrameGapMs = std::abs(PtsToMs(drmBuffer->frame->pts - m_pCurrentlyDisplayed->frame->pts));
527 } else
529 }
530
532 // check if playback shall start
533 if (PtsToMs(drmBuffer->frame->pts) < m_schedulePlaybackStartAtPtsMs) {
534 drmBuffer->PresentationFinished();
535 return true;
536 } else {
538 m_videoPlaybackPaused = false;
539 }
540 } else if (!m_displayOneFrameThenPause && !IsStillpicture()) {
541 // A/V sync
542 int64_t audioPtsMs = m_pAudio->GetHardwareOutputPtsMs();
543 int64_t videoPtsMs = PtsToMs(drmBuffer->frame->pts);
544
545 if (audioPtsMs != AV_NOPTS_VALUE) {
546 int audioBehindVideoByMs = videoPtsMs - audioPtsMs - m_pDevice->GetVideoAudioDelayMs();
547
548 bool skipSync = m_scheduleResyncAtPtsMs != AV_NOPTS_VALUE;
550 m_scheduleResyncAtPtsMs <= videoPtsMs &&
551 std::abs(PtsToMs(m_scheduleResyncAtPtsMs) - PtsToMs(videoPtsMs)) < AV_SYNC_BORDER_MS) {
552
553 LOGDEBUG2(L_AV_SYNC, "videorender: resync schedule arrived at %s, current audio pts %s video pts %s",
555 m_eventQueue.push_back(ResyncEvent{});
557 }
558
559 if (m_resumeAudioScheduled && audioBehindVideoByMs >= 0 && !skipSync) { // resume audio from pause
560 LOGDEBUG2(L_AV_SYNC, "videorender: resuming audio playback: video %s, audio %s", Timestamp2String(videoPtsMs, 1), Timestamp2String(audioPtsMs, 1));
561 m_pAudio->SetPaused(false);
563 } else if (!m_pAudio->IsPaused() && !skipSync && audioBehindVideoByMs > AV_SYNC_THRESHOLD_AUDIO_BEHIND_VIDEO_MS) { // duplicate frame
564 LogDroppedDuped(audioPtsMs, videoPtsMs, audioBehindVideoByMs);
565 m_framePresentationCounter++; // display the current video frame one period longer
566 } else if (!m_pAudio->IsPaused() && !skipSync && audioBehindVideoByMs < -AV_SYNC_THRESHOLD_AUDIO_AHEAD_VIDEO_MS && !m_lastFrameWasDropped) { // drop frame
567 // Drop max every second frame. Otherwise, the buffer gets drained immediately, if multiple frames in a row are dropped.
568 LogDroppedDuped(audioPtsMs, videoPtsMs, audioBehindVideoByMs);
569
570 if (pipBuf)
571 pipBuf->PresentationFinished();
572
573 drmBuffer->PresentationFinished();
574 m_framePresentationCounter--; // skip this pageflip
576
577 return true;
578 }
579
581 }
582
584 m_pAudio->DropSamplesOlderThanPtsMs(drmBuffer->frame->pts * 1000 * av_q2d(m_timebase));
585 }
586
590 }
591
592 pageFlipDone = PageFlip(drmBuffer, pipBuf);
593
596
597 m_lastFrameWasDropped = false;
598 m_pCurrentlyDisplayed = drmBuffer;
600 // display the current frame again in trick speed mode or for A/V syncing
601 pageFlipDone = PageFlip(m_pCurrentlyDisplayed, pipBuf);
602 } else if ((m_pBufOsd && m_pBufOsd->IsDirty()) || pipBuf) {
603 pageFlipDone = PageFlip(NULL, pipBuf);
604 }
605
606 if (pipBuf) {
609
611 }
612
615
617
618 return pageFlipDone;
619}
620
622{
623 LOGDEBUG2(L_DRM, "videorender: %s: closing, set a black FB", __FUNCTION__);
624
625 PageFlip(&m_bufBlack, NULL);
626
628 av_frame_free(&m_pCurrentlyDisplayed->frame);
630 m_pCurrentlyDisplayed = nullptr;
631 }
632}
633
634int64_t cVideoRender::PtsToMs(int64_t pts)
635{
636 std::lock_guard<std::mutex> lock(m_timebaseMutex);
637
638 return pts * 1000 * av_q2d(m_timebase);
639}
640
645{
646 return m_pDrmDevice->HandleEvent();
647}
648
649/*****************************************************************************
650 * OSD
651 ****************************************************************************/
652
657{
658#ifdef USE_GLES
659 if (m_disableOglOsd) {
660 memset((void *)m_pBufOsd->Plane(0), 0,
661 (size_t)(m_pBufOsd->Pitch(0) * m_pBufOsd->Height()));
662 } else {
663 cDrmBuffer *buf;
664
665 EGL_CHECK(eglSwapBuffers(m_pDrmDevice->EglDisplay(), m_pDrmDevice->EglSurface()));
666 m_pNextBo = gbm_surface_lock_front_buffer(m_pDrmDevice->GbmSurface());
667 assert(m_pNextBo);
668
669 buf = m_pDrmDevice->GetBufFromBo(m_pNextBo);
670 if (!buf) {
671 LOGERROR("videorender: %s: Failed to get GL buffer", __FUNCTION__);
672 return;
673 }
674
675 m_pBufOsd = buf;
676
677 // release old buffer for writing again
678 if (m_bo)
679 gbm_surface_release_buffer(m_pDrmDevice->GbmSurface(), m_bo);
680
681 // rotate bos and create and keep bo as m_pOldBo to make it free'able
682 m_pOldBo = m_bo;
683 m_bo = m_pNextBo;
684
685 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));
686 }
687#else
688 memset((void *)m_pBufOsd->Plane(0), 0,
689 (size_t)(m_pBufOsd->Pitch(0) * m_pBufOsd->Height()));
690#endif
691
693 m_osdShown = false;
694}
695
696#define MIN(a, b) ((a) < (b) ? (a) : (b))
697
710void cVideoRender::OsdDrawARGB(int xi, int yi,
711 int width, int height, int pitch,
712 const uint8_t * argb, int x, int y)
713{
714#ifdef USE_GLES
715 if (m_disableOglOsd) {
716 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__,
717 width, height, pitch, argb, x, y, m_pBufOsd->Pitch(0), xi, yi);
718 for (int i = 0; i < height; ++i) {
719 memcpy(m_pBufOsd->Plane(0) + x * 4 + (i + y) * m_pBufOsd->Pitch(0),
720 argb + i * pitch, MIN((size_t)pitch, m_pBufOsd->Pitch(0)));
721 }
722 } else {
723 cDrmBuffer *buf;
724
725 EGL_CHECK(eglSwapBuffers(m_pDrmDevice->EglDisplay(), m_pDrmDevice->EglSurface()));
726 m_pNextBo = gbm_surface_lock_front_buffer(m_pDrmDevice->GbmSurface());
727 assert(m_pNextBo);
728
729 buf = m_pDrmDevice->GetBufFromBo(m_pNextBo);
730 if (!buf) {
731 LOGERROR("videorender: %s: Failed to get GL buffer", __FUNCTION__);
732 return;
733 }
734
735 m_pBufOsd = buf;
736
737 // release old buffer for writing again
738 if (m_bo)
739 gbm_surface_release_buffer(m_pDrmDevice->GbmSurface(), m_bo);
740
741 // rotate bos and create and keep bo as m_pOldBo to make it free'able
742 m_pOldBo = m_bo;
743 m_bo = m_pNextBo;
744
745 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));
746 }
747#else
748 // suppress unused variable warnings ...
749 (void) xi;
750 (void) yi;
751 (void) width;
752
753 for (int i = 0; i < height; ++i) {
754 memcpy(m_pBufOsd->Plane(0) + x * 4 + (i + y) * m_pBufOsd->Pitch(0),
755 argb + i * pitch, (size_t)pitch);
756 }
757#endif
759 m_osdShown = true;
760}
761
762/*****************************************************************************
763 * Thread
764 ****************************************************************************/
765
770{
771 LOGDEBUG("videorender: %s", __FUNCTION__);
772
773 Reset();
774 if (m_pDisplayThread->Active())
776}
777
781static void ReleaseFrame( __attribute__ ((unused)) void *opaque, uint8_t *data)
782{
783 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)data;
784
785 av_free(primedata);
786}
787
797
798/*****************************************************************************
799 * Buffer reuse strategy: use-once
800 ****************************************************************************/
802{
803 cDrmBuffer *buf = pool->FindUninitilized();
804
805 if (buf)
806 buf->SetDestroyAfterUse(true);
807
808 return buf;
809}
810
811/*****************************************************************************
812 * Buffer reuse strategy: reuse
813 ****************************************************************************/
815{
816 cDrmBuffer *buf = pool->FindByDmaBufHandle(primedata->objects[0].fd);
817
818 if (buf)
819 return buf;
820 else
821 return pool->FindUninitilized();
822}
823
825{
827
828 if (buf)
829 return buf;
830 else
831 return pool->FindUninitilized();
832}
833
834/*****************************************************************************
835 * Decoding strategy: software
836 ****************************************************************************/
837AVFrame *cDecodingStrategySoftware::PrepareDrmBuffer(cDrmBuffer *buf, int drmDeviceFd, AVFrame *inframe)
838{
839 if (!buf->IsDirty()) {
840 buf->Setup(drmDeviceFd, inframe->width, inframe->height, DRM_FORMAT_NV12, nullptr, true);
841
842 int dmaBufHandle;
843 if (drmPrimeHandleToFD(drmDeviceFd, buf->PrimeHandle(0), DRM_CLOEXEC | DRM_RDWR, &dmaBufHandle))
844 LOGFATAL("videorender: %s: Failed to retrieve the Prime FD (%d): %m", __FUNCTION__, errno);
845
846 buf->SetDmaBufHandle(dmaBufHandle);
847 }
848
849 for (int i = 0; i < inframe->height; ++i)
850 memcpy(buf->Plane(0) + i * buf->Pitch(0), inframe->data[0] + i * inframe->linesize[0], inframe->linesize[0]);
851
852 for (int i = 0; i < inframe->height / 2; ++i)
853 memcpy(buf->Plane(1) + i * buf->Pitch(1), inframe->data[1] + i * inframe->linesize[1], inframe->linesize[1]);
854
855 AVFrame *frame = av_frame_alloc();
856 frame->pts = inframe->pts;
857 frame->width = inframe->width;
858 frame->height = inframe->height;
859 frame->format = AV_PIX_FMT_DRM_PRIME;
860 frame->sample_aspect_ratio = inframe->sample_aspect_ratio;
861
862 frame->format = AV_PIX_FMT_DRM_PRIME;
863 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)av_mallocz(sizeof(AVDRMFrameDescriptor));
864 primedata->objects[0].fd = buf->DmaBufHandle();
865 frame->data[0] = (uint8_t *)primedata;
866 frame->buf[0] = av_buffer_create((uint8_t *)primedata, sizeof(*primedata), ReleaseFrame, NULL, AV_BUFFER_FLAG_READONLY);
867
868 av_frame_free(&inframe);
869
870 return frame;
871}
872
873/*****************************************************************************
874 * Decoding strategy: hardware
875 ****************************************************************************/
876AVFrame *cDecodingStrategyHardware::PrepareDrmBuffer(cDrmBuffer *buf, int drmDeviceFd, AVFrame *frame)
877{
878 if (!buf->IsDirty()) {
879 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)frame->data[0];
880 buf->Setup(drmDeviceFd, frame->width, frame->height, 0, primedata, false);
881 }
882
883 return frame;
884}
885
890
895
900 AVFrame *frame,
901 bool trickspeed,
902 std::atomic<cBufferStrategy*> &bufferReuseStrategy,
903 std::atomic<cDecodingStrategy*> &decodingStrategy,
904 cQueue<cDrmBuffer>* drmBufferQueue,
905 cDrmBufferPool *drmBufferPool)
906{
907 if (bufferReuseStrategy == nullptr) {
908 if (trickspeed)
909 bufferReuseStrategy = new cBufferStrategyUseOnce();
910 else if (frame->format == AV_PIX_FMT_DRM_PRIME)
911 bufferReuseStrategy = new cBufferStrategyReuseHardware();
912 else
913 bufferReuseStrategy = new cBufferStrategyReuseSoftware();
914 }
915
916 if (decodingStrategy == nullptr) {
917 if (frame->format == AV_PIX_FMT_DRM_PRIME)
918 decodingStrategy = new cDecodingStrategyHardware();
919 else
920 decodingStrategy = new cDecodingStrategySoftware();
921 }
922
923 // 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.
924 //
925 // @note: This is the only place outside of the display thread, where the video pts is set
926 // (except setting it to AV_NOPTS_VALUE in cSofthdDevice::Clear() and SetState(STOP))
927 // We only store here, if the stream recently started and the clock wasn't set already in the display thread
928
929 if (GetVideoClock() == AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE)
930 SetVideoClock(frame->pts);
931
932 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)frame->data[0];
933 cDrmBuffer *buf = bufferReuseStrategy.load()->GetBuffer(drmBufferPool, primedata);
934
935 if (!buf)
936 LOGFATAL("videorender: %s: no free DRM buffer found. This is a bug.", __FUNCTION__);
937
938 frame = decodingStrategy.load()->PrepareDrmBuffer(buf, m_pDrmDevice->Fd(), frame);
939
940 buf->frame = frame;
941 buf->SetPresentationPending(true);
942
943 drmBufferQueue->Push(buf);
944}
945
954{
956 return AV_NOPTS_VALUE;
957
958 std::lock_guard<std::mutex> lock(m_timebaseMutex);
959
960 return GetVideoClock() * 1000 * av_q2d(m_timebase);
961}
962
967{
968 m_startCounter = 0;
969 LOGDEBUG("videorender: %s: reset m_startCounter %d TrickSpeed %d", __FUNCTION__, m_startCounter, IsTrickSpeed());
970}
971
983
991void cVideoRender::SetTrickSpeed(double speed, bool active, bool forward)
992{
993 LOGDEBUG2(L_TRICK, "videorender: %s: set trick speed %.3f %s %s", __FUNCTION__, speed, speed > 1.0 ? "fast" : "slow", forward ? "forward" : "backward");
995 m_trickspeedFactor = speed;
996 m_trickspeed = active;
997 m_forwardTrickspeed = forward;
998}
999
1008{
1009 if (!IsTrickSpeed())
1010 return 1;
1011
1012 // Calculate the expected number of display refreshes for this frame
1013 double interFrameGapSec = interFrameGapMs / 1000.0;
1014 double refreshPeriodSec = 1.0 / m_refreshRateHz;
1015 int displayCount = std::max(1, static_cast<int>(std::round(interFrameGapSec / refreshPeriodSec / m_trickspeedFactor)));
1016
1017 return displayCount;
1018}
1019
1020/*****************************************************************************
1021 * Grabbing
1022 ****************************************************************************/
1023
1031{
1032 int timeoutMs = 50;
1033 cMutex mutex;
1034 mutex.Lock();
1035 m_startgrab = true;
1036 int err = 0;
1037
1038 if (!m_grabCond.TimedWait(mutex, timeoutMs)) {
1039 LOGWARNING("videorender: %s: timed out after %dms", __FUNCTION__, timeoutMs);
1040 err = 1;
1041 }
1042
1043 m_startgrab = false;
1044 return err;
1045}
1046
1053{
1054 if (!m_startgrab)
1055 return;
1056
1057 if (m_pBufOsd && m_osdShown) {
1058 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger osd grab arrived", __FUNCTION__);
1059 cDrmBuffer *osdBuf = new cDrmBuffer(m_pBufOsd);
1060 m_grabOsd.SetDrmBuf(osdBuf);
1061 }
1062
1064 if (pbuf) {
1065 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger video grab arrived", __FUNCTION__);
1066 cDrmBuffer *videoBuf = new cDrmBuffer(pbuf);
1067 m_grabVideo.SetDrmBuf(videoBuf);
1068 }
1069
1071 if (pipBuf && grabPip) {
1072 LOGDEBUG2(L_GRAB, "videorender: %s: Trigger pip grab arrived", __FUNCTION__);
1073 cDrmBuffer *pipVideoBuf = new cDrmBuffer(pipBuf);
1074 m_grabPip.SetDrmBuf(pipVideoBuf);
1075 }
1076
1077 m_grabCond.Broadcast();
1078}
1079
1092
1100void cVideoRender::GetStats(int *duped, int *dropped, int *counter)
1101{
1102 *duped = m_framesDuped;
1103 *dropped = m_framesDropped;
1104 *counter = m_startCounter;
1105}
1106
1107/*****************************************************************************
1108 * Setup and initialization
1109 ****************************************************************************/
1110
1118void cVideoRender::SetScreenSize(int width, int height, double refreshRateHz)
1119{
1120 m_refreshRateHz = refreshRateHz;
1121 m_pDevice->SetScreenSize(width, height, refreshRateHz);
1122}
1123
1128{
1129 if (m_pDrmDevice->Init())
1130 LOGFATAL("videorender: %s: failed", __FUNCTION__);
1131
1133
1134 cDrmPlane *videoPlane = m_pDrmDevice->VideoPlane();
1135 cDrmPlane *osdPlane = m_pDrmDevice->OsdPlane();
1136
1137 // osd FB
1138#ifndef USE_GLES
1139 if (!m_pBufOsd)
1140 m_pBufOsd = new cDrmBuffer();
1141
1142 m_pBufOsd->Setup(m_pDrmDevice->Fd(), m_pDrmDevice->DisplayWidth(), m_pDrmDevice->DisplayHeight(), DRM_FORMAT_ARGB8888, NULL, false);
1143#else
1144 if (m_disableOglOsd) {
1145 if (!m_pBufOsd)
1146 m_pBufOsd = new cDrmBuffer();
1147
1148 m_pBufOsd->Setup(m_pDrmDevice->Fd(), m_pDrmDevice->DisplayWidth(), m_pDrmDevice->DisplayHeight(), DRM_FORMAT_ARGB8888, NULL, false);
1149 }
1150#endif
1151
1152 // black fb
1153 LOGDEBUG2(L_DRM, "videorender: %s: Try to create a black FB", __FUNCTION__);
1154 m_bufBlack.Setup(m_pDrmDevice->Fd(), m_pDrmDevice->DisplayWidth(), m_pDrmDevice->DisplayHeight(), DRM_FORMAT_NV12, NULL, false);
1156
1157 // save actual modesetting
1159
1160 drmModeAtomicReqPtr modeReq;
1161 const uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
1162 uint32_t modeID = 0;
1163
1164 if (m_pDrmDevice->CreatePropertyBlob(&modeID) != 0)
1165 LOGFATAL("videorender: %s: Failed to create mode property blob.", __FUNCTION__);
1166 if (!(modeReq = drmModeAtomicAlloc()))
1167 LOGFATAL("videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
1168
1170 DRM_MODE_OBJECT_CRTC, "MODE_ID", modeID);
1172 DRM_MODE_OBJECT_CONNECTOR, "CRTC_ID", m_pDrmDevice->CrtcId());
1174 DRM_MODE_OBJECT_CRTC, "ACTIVE", 1);
1175
1176 // Osd plane
1177 // 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,
1178 // but initially move the OSD behind the VIDEO
1179#ifndef USE_GLES
1180 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
1182 0, 0, m_pBufOsd->Width(), m_pBufOsd->Height());
1183
1184 osdPlane->SetPlane(modeReq);
1185#else
1186 if (m_disableOglOsd) {
1187 osdPlane->SetParams(m_pDrmDevice->CrtcId(), m_pBufOsd->Id(),
1189 0, 0, m_pBufOsd->Width(), m_pBufOsd->Height());
1190
1191 osdPlane->SetPlane(modeReq);
1192 }
1193#endif
1194 if (m_pDrmDevice->UseZpos()) {
1195 videoPlane->SetZpos(m_pDrmDevice->ZposOverlay());
1196 videoPlane->SetPlaneZpos(modeReq);
1197#ifdef USE_GLES
1198 osdPlane->SetZpos(m_pDrmDevice->ZposPrimary());
1199 osdPlane->SetPlaneZpos(modeReq);
1200#endif
1201 }
1202
1203 // Black buffer for video plane
1204 videoPlane->SetParams(m_pDrmDevice->CrtcId(), m_bufBlack.Id(),
1206 0, 0, m_bufBlack.Width(), m_bufBlack.Height());
1207
1208 videoPlane->SetPlane(modeReq);
1209
1210 if (drmModeAtomicCommit(m_pDrmDevice->Fd(), modeReq, flags, NULL) != 0) {
1211#ifndef USE_GLES
1212 osdPlane->DumpParameters("osd");
1213#endif
1214 videoPlane->DumpParameters("video");
1215
1216 drmModeAtomicFree(modeReq);
1217 LOGFATAL("videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
1218 }
1219
1220 drmModeAtomicFree(modeReq);
1221
1222 m_osdShown = false;
1223
1224 // init variables page flip
1226
1227 m_pDisplayThread = new cDisplayThread(this);
1228}
1229
1234{
1235 cDrmPlane *videoPlane = m_pDrmDevice->VideoPlane();
1236 cDrmPlane *osdPlane = m_pDrmDevice->OsdPlane();
1237
1239
1240 // restore saved CRTC configuration
1242
1243 videoPlane->FreeProperties();
1244 osdPlane->FreeProperties();
1245
1247#ifdef USE_GLES
1248 if (m_disableOglOsd) {
1249 if (m_pBufOsd) {
1250 m_pBufOsd->Destroy();
1251 delete m_pBufOsd;
1252 }
1253 } else {
1254 if (m_pNextBo)
1255 gbm_bo_destroy(m_pNextBo);
1256 if (m_pOldBo)
1257 gbm_bo_destroy(m_pOldBo);
1258 }
1259#else
1260 if (m_pBufOsd) {
1261 m_pBufOsd->Destroy();
1262 delete m_pBufOsd;
1263 }
1264#endif
1265
1267}
1268
1275{
1276 m_videoRect.Set(rect.Point(), rect.Size());
1277
1278 if (m_videoRect.IsEmpty())
1279 m_videoIsScaled = false;
1280 else
1281 m_videoIsScaled = true;
1282
1283 LOGDEBUG("videorender: %s: %d %d %d %d%s", __FUNCTION__, rect.X(), rect.Y(), rect.Width(), rect.Height(), m_videoIsScaled ? ", video is scaled" : "");
1284}
1285
1290{
1291 for (Event event : m_eventQueue)
1293
1294 m_eventQueue.clear();
1295}
1296
Audio and alsa module header file.
#define AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
Definition audio.h:42
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:65
void Stop(void)
Definition threads.cpp:112
cDrmBuffer * FindUninitilized(void)
cDrmBuffer * FindByDmaBufHandle(int)
cDrmBuffer * FindNoPresentationPending(void)
void DestroyAllExcept(cDrmBuffer *)
void MarkClean(void)
Definition drmbuffer.h:72
void SetSizeOnScreen(int x, int y, int w, int h)
Definition drmbuffer.h:102
uint32_t Pitch(int idx)
Definition drmbuffer.h:93
int DmaBufHandle(void)
Definition drmbuffer.h:82
void Setup(int, uint32_t, uint32_t, uint32_t, AVDRMFrameDescriptor *, bool)
Setup the buffer.
uint32_t Width(void)
Definition drmbuffer.h:64
uint32_t Height(void)
Definition drmbuffer.h:66
uint8_t * Plane(int idx)
Definition drmbuffer.h:86
void MarkDirty(void)
Definition drmbuffer.h:73
void FillBlack(void)
Color the buffer black.
uint32_t PrimeHandle(int idx)
Definition drmbuffer.h:87
AVFrame * frame
associated AVFrame
Definition drmbuffer.h:99
int Id(void)
Definition drmbuffer.h:77
bool IsDirty(void)
Definition drmbuffer.h:71
void SetPresentationPending(bool pending)
Definition drmbuffer.h:75
void PresentationFinished(void)
void Destroy(void)
Clear and destroy the buffer object and its parameters.
void SetDmaBufHandle(uint32_t fd)
Definition drmbuffer.h:83
void SetDestroyAfterUse(bool val)
Definition drmbuffer.h:100
int CreatePropertyBlob(uint32_t *)
Creates a property blob.
int Fd(void)
Definition drmdevice.h:52
bool HasPipPlane(void)
Definition drmdevice.h:69
int SetPropertyRequest(drmModeAtomicReqPtr, uint32_t, uint32_t, const char *, uint64_t)
Add a property to a request.
uint64_t DisplayHeight(void)
Definition drmdevice.h:59
int HandleEvent(void)
Polls for a drm event.
uint64_t DisplayWidth(void)
Definition drmdevice.h:58
uint64_t ZposPrimary(void)
Definition drmdevice.h:64
uint64_t ZposOverlay(void)
Definition drmdevice.h:63
cDrmPlane * PipPlane(void)
Definition drmdevice.h:68
void SaveCrtc(void)
Saves information of a CRTC.
uint32_t CrtcId(void)
Definition drmdevice.h:61
void RestoreCrtc(void)
Restore information of a CRTC.
void Close(void)
Close drm file handle.
cDrmPlane * OsdPlane(void)
Definition drmdevice.h:66
cDrmPlane * VideoPlane(void)
Definition drmdevice.h:67
uint32_t ConnectorId(void)
Definition drmdevice.h:56
int Init(void)
Initiate the drm device.
void InitEvent(void)
Init the event context.
int UseZpos(void)
Definition drmdevice.h:62
cDrmPlane - DRM plane class
Definition drmplane.h:29
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:98
void ClearPlane(drmModeAtomicReqPtr)
Clear plane from drm.
Definition drmplane.cpp:177
uint64_t GetZpos(void)
Definition drmplane.h:59
void SetZpos(uint64_t zpos)
Definition drmplane.h:60
uint64_t GetFbId(void)
Definition drmplane.h:50
void FreeProperties(void)
Free the previously filled plane properties.
Definition drmplane.cpp:65
void SetPlane(drmModeAtomicReqPtr)
Set all other plane properties.
Definition drmplane.cpp:156
void SetPlaneZpos(drmModeAtomicReqPtr)
Set the plane zpos property.
Definition drmplane.cpp:146
uint32_t GetId(void)
Definition drmplane.h:45
void DumpParameters(const char *)
Dump the plane parameter modesetting values.
Definition drmplane.cpp:218
void FreeDrmBuf(void)
Free the grab buffer.
Definition grab.cpp:293
void SetDrmBuf(cDrmBuffer *)
Set the grab buffer and the dimensions how it is presented on the screen.
Definition grab.cpp:312
cDrmBuffer * GetDrmBuf(void)
Definition grab.h:70
Thread-safe queue class.
Definition queue.h:36
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:888
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition audio.cpp:570
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
Definition audio.cpp:849
void SetPaused(bool)
Set audio playback paused state.
Definition audio.cpp:948
bool IsPaused(void)
Definition audio.h:59
int ConfigPipAltTopPercent
0 = aligned to top, 100 = aligned to bottom
Definition config.h:75
int ConfigPipLeftPercent
0 = aligned to left, 100 = aligned to right
Definition config.h:68
int ConfigPipAltLeftPercent
0 = aligned to left, 100 = aligned to right
Definition config.h:74
int ConfigPipAltScalePercent
alternative scale factor of pip video
Definition config.h:73
int ConfigPipTopPercent
0 = aligned to top, 100 = aligned to bottom
Definition config.h:69
int ConfigPipScalePercent
scale factor of pip video
Definition config.h:67
int ConfigPipUseAlt
Definition config.h:71
int GetVideoAudioDelayMs(void)
void SetDrmCanDisplayPip(bool canDisplay)
bool IsBufferingThresholdReached(void)
Check if the buffering threshold has been reached.
cVideoStream * VideoStream(void)
void SetScreenSize(int, int, double)
Set the screen size.
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)
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
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
std::atomic< bool > m_resumeAudioScheduled
set, if audio resume is scheduled after a pause
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)
cDisplayThread * m_pDisplayThread
pointer to display thread
std::atomic< int > m_framePresentationCounter
number of times the current frame has to be shown (for slow-motion)
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.
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)
cVideoRender destructor
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)
bool IsStillpicture(void)
cSoftHdAudio * m_pAudio
pointer to cSoftHdAudio
AVRational m_timebase
timebase used for pts, set by first RenderFrame()
void PushMainFrame(AVFrame *)
void SetVideoOutputPosition(const cRect &)
Set size and position of the video on the screen.
cVideoRender(cSoftHdDevice *)
cVideoRender constructor
int GetFrameFlags(AVFrame *)
Get frame flags.
int m_pipTopPercent
top margin for pip
cDrmBuffer * m_pBufOsd
pointer to osd drm buffer 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.
void LogDroppedDuped(int64_t, int64_t, int)
Log A/V sync debug message.
bool DisplayFrame()
Display the frame (video and/or osd)
bool IsTrickSpeed(void)
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)
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)
bool PageFlip(cDrmBuffer *, cDrmBuffer *)
Do the pageflip.
int m_pipLeftPercent
left margin for pip
cDrmBufferPool m_drmBufferPool
pool of drm buffers
std::atomic< int64_t > m_schedulePlaybackStartAtPtsMs
if set, frames with PTS older than this will be dropped
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
int GetFramePresentationCount(int64_t)
Get the number of times the current frame shall be presented in trickspeed mode.
void ExitDisplayThread(void)
Stop display thread.
void SetTrickSpeed(double, bool, bool)
Set the trickspeed parameters.
cDrmBuffer m_bufBlack
black drm buffer object
size_t GetAvPacketsFilled(void)
Definition videostream.h:73
SoftHdDevice config header file.
DRM device header file.
State machine and event header file.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent > Event
Definition event.h:79
@ VIDEO
Definition event.h:25
Grabber header file.
Logger class header file.
#define LOGDEBUG2
Definition logger.h:45
#define LOGDEBUG
Definition logger.h:44
#define LOGERROR
Definition logger.h:41
#define L_DRM
Definition logger.h:57
#define L_GRAB
Definition logger.h:66
#define L_AV_SYNC
Definition logger.h:54
#define LOGWARNING
Definition logger.h:42
#define L_PACKET
Definition logger.h:65
#define L_OPENGL
Definition logger.h:62
#define LOGFATAL
Logger macros.
Definition logger.h:40
#define L_TRICK
Definition logger.h:60
#define L_OSD
Definition logger.h:56
Misc function header file.
#define AV_NOPTS_VALUE
Definition misc.h:69
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Workaround for av_err2str() not working with C++.
Definition misc.h:119
Thread-safe queue header file.
Device class header file.
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:62
#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:63
Videostream class header file.