38#include <libavcodec/avcodec.h>
39#include <libavutil/hwcontext_drm.h>
42#include <drm_fourcc.h>
44#include <vdr/thread.h>
45#include <xf86drmMode.h>
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)
77 m_disableOglOsd =
m_pConfig->ConfigDisableOglOsd;
142 if (!frame || dispWidth == 0 || dispHeight == 0)
143 return { dispX, dispY, dispWidth, dispHeight };
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;
151 double picWidthD = dispWidth;
152 double picHeightD = dispHeight;
154 if (dispAspect > frameAspect) {
156 picWidthD = dispHeight * frameAspect;
157 if (picWidthD <= 0 || picWidthD > dispWidth)
158 picWidthD = dispWidth;
161 picHeightD = dispWidth / frameAspect;
162 if (picHeightD <= 0 || picHeightD > dispHeight)
163 picHeightD = dispHeight;
167 uint64_t picWidth = std::llround(std::max(0.0, picWidthD));
168 uint64_t picHeight = std::llround(std::max(0.0, picHeightD));
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));
175 return { posX, posY, picWidth, picHeight };
191 AVFrame *frame = buf->
frame;
214 fittedRect.
x, fittedRect.
y, fittedRect.
w, fittedRect.
h,
243 LOGDEBUG2(
L_DRM,
"videorender: %s: SetPlaneZpos: video->plane_id %d -> zpos %" PRIu64
", osd->plane_id %d -> zpos %" PRIu64
"", __FUNCTION__,
275 AVFrame *frame = buf->
frame;
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);
304 uint64_t crtcW = std::llround(crtcWD);
305 uint64_t crtcH = std::llround(crtcHD);
307 double spaceW = dispWidth - crtcW - centerOffsetX;
308 double spaceH = dispHeight - crtcH - centerOffsetY;
315 crtcX, crtcY, crtcW, crtcH,
334 MODESET_OSD = (1 << 0),
335 MODESET_VIDEO = (1 << 1),
336 MODESET_PIP = (1 << 2)
344 drmModeAtomicReqPtr modeReq;
345 uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT;
347 if (!(modeReq = drmModeAtomicAlloc())) {
348 LOGERROR(
"videorender: %s: cannot allocate atomic request (%d): %m", __FUNCTION__, errno);
358 modeSet |= MODESET_VIDEO;
363 if (pipPlane->
GetId()) {
369 modeSet |= MODESET_PIP;
375 modeSet |= MODESET_OSD;
381 drmModeAtomicFree(modeReq);
386 if (drmModeAtomicCommit(fdDrm, modeReq, flags, NULL) != 0) {
387 if (modeSet & MODESET_OSD)
389 if (modeSet & MODESET_VIDEO)
391 if (modeSet & MODESET_PIP)
394 drmModeAtomicFree(modeReq);
395 LOGERROR(
"videorender: %s: page flip failed (%d): %m", __FUNCTION__, errno);
399 drmModeAtomicFree(modeReq);
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",
423 audioBehindVideoByMs);
425 if (audioBehindVideoByMs > 0)
441 if (!frame || !frame->opaque_ref)
444 int *frameFlags = (
int *)frame->opaque_ref->data;
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__);
464 frameFlags = (
int *)frame->opaque_ref->data;
479 if (buf && buf->
frame)
480 av_frame_free(&buf->
frame);
481 if (pipBuf && pipBuf->
frame)
482 av_frame_free(&pipBuf->
frame);
487 LOGERROR(
"threads: display thread: drmHandleEvent failed!");
490 if (buf && buf->
frame) {
521 bool pageFlipDone =
false;
553 LOGDEBUG2(
L_AV_SYNC,
"videorender: resync schedule arrived at %s, current audio pts %s video pts %s",
592 pageFlipDone =
PageFlip(drmBuffer, pipBuf);
603 pageFlipDone =
PageFlip(NULL, pipBuf);
623 LOGDEBUG2(
L_DRM,
"videorender: %s: closing, set a black FB", __FUNCTION__);
659 if (m_disableOglOsd) {
666 m_pNextBo = gbm_surface_lock_front_buffer(
m_pDrmDevice->GbmSurface());
671 LOGERROR(
"videorender: %s: Failed to get GL buffer", __FUNCTION__);
679 gbm_surface_release_buffer(
m_pDrmDevice->GbmSurface(), m_bo);
696#define MIN(a, b) ((a) < (b) ? (a) : (b))
711 int width,
int height,
int pitch,
712 const uint8_t * argb,
int x,
int y)
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__,
718 for (
int i = 0; i < height; ++i) {
726 m_pNextBo = gbm_surface_lock_front_buffer(
m_pDrmDevice->GbmSurface());
731 LOGERROR(
"videorender: %s: Failed to get GL buffer", __FUNCTION__);
739 gbm_surface_release_buffer(
m_pDrmDevice->GbmSurface(), m_bo);
753 for (
int i = 0; i < height; ++i) {
755 argb + i * pitch, (
size_t)pitch);
771 LOGDEBUG(
"videorender: %s", __FUNCTION__);
781static void ReleaseFrame( __attribute__ ((unused))
void *opaque, uint8_t *data)
783 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)data;
840 buf->
Setup(drmDeviceFd, inframe->width, inframe->height, DRM_FORMAT_NV12,
nullptr,
true);
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);
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]);
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]);
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;
862 frame->format = AV_PIX_FMT_DRM_PRIME;
863 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)av_mallocz(
sizeof(AVDRMFrameDescriptor));
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);
868 av_frame_free(&inframe);
879 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)frame->data[0];
880 buf->
Setup(drmDeviceFd, frame->width, frame->height, 0, primedata,
false);
902 std::atomic<cBufferStrategy*> &bufferReuseStrategy,
903 std::atomic<cDecodingStrategy*> &decodingStrategy,
907 if (bufferReuseStrategy ==
nullptr) {
910 else if (frame->format == AV_PIX_FMT_DRM_PRIME)
916 if (decodingStrategy ==
nullptr) {
917 if (frame->format == AV_PIX_FMT_DRM_PRIME)
932 AVDRMFrameDescriptor *primedata = (AVDRMFrameDescriptor *)frame->data[0];
933 cDrmBuffer *buf = bufferReuseStrategy.load()->GetBuffer(drmBufferPool, primedata);
936 LOGFATAL(
"videorender: %s: no free DRM buffer found. This is a bug.", __FUNCTION__);
938 frame = decodingStrategy.load()->PrepareDrmBuffer(buf,
m_pDrmDevice->
Fd(), frame);
943 drmBufferQueue->
Push(buf);
993 LOGDEBUG2(
L_TRICK,
"videorender: %s: set trick speed %.3f %s %s", __FUNCTION__, speed, speed > 1.0 ?
"fast" :
"slow", forward ?
"forward" :
"backward");
1013 double interFrameGapSec = interFrameGapMs / 1000.0;
1015 int displayCount = std::max(1,
static_cast<int>(std::round(interFrameGapSec / refreshPeriodSec /
m_trickspeedFactor)));
1017 return displayCount;
1038 if (!
m_grabCond.TimedWait(mutex, timeoutMs)) {
1039 LOGWARNING(
"videorender: %s: timed out after %dms", __FUNCTION__, timeoutMs);
1058 LOGDEBUG2(
L_GRAB,
"videorender: %s: Trigger osd grab arrived", __FUNCTION__);
1065 LOGDEBUG2(
L_GRAB,
"videorender: %s: Trigger video grab arrived", __FUNCTION__);
1071 if (pipBuf && grabPip) {
1072 LOGDEBUG2(
L_GRAB,
"videorender: %s: Trigger pip grab arrived", __FUNCTION__);
1130 LOGFATAL(
"videorender: %s: failed", __FUNCTION__);
1144 if (m_disableOglOsd) {
1153 LOGDEBUG2(
L_DRM,
"videorender: %s: Try to create a black FB", __FUNCTION__);
1160 drmModeAtomicReqPtr modeReq;
1161 const uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
1162 uint32_t 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);
1170 DRM_MODE_OBJECT_CRTC,
"MODE_ID", modeID);
1174 DRM_MODE_OBJECT_CRTC,
"ACTIVE", 1);
1186 if (m_disableOglOsd) {
1210 if (drmModeAtomicCommit(
m_pDrmDevice->
Fd(), modeReq, flags, NULL) != 0) {
1216 drmModeAtomicFree(modeReq);
1217 LOGFATAL(
"videorender: %s: cannot set atomic mode (%d): %m", __FUNCTION__, errno);
1220 drmModeAtomicFree(modeReq);
1248 if (m_disableOglOsd) {
1255 gbm_bo_destroy(m_pNextBo);
1257 gbm_bo_destroy(m_pOldBo);
1283 LOGDEBUG(
"videorender: %s: %d %d %d %d%s", __FUNCTION__, rect.X(), rect.Y(), rect.Width(), rect.Height(),
m_videoIsScaled ?
", video is scaled" :
"");
Audio and alsa module header file.
#define AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
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
cDrmBuffer * FindUninitilized(void)
cDrmBuffer * FindByDmaBufHandle(int)
cDrmBuffer * FindNoPresentationPending(void)
void DestroyAllExcept(cDrmBuffer *)
void SetSizeOnScreen(int x, int y, int w, int h)
void Setup(int, uint32_t, uint32_t, uint32_t, AVDRMFrameDescriptor *, bool)
Setup the buffer.
void FillBlack(void)
Color the buffer black.
uint32_t PrimeHandle(int idx)
AVFrame * frame
associated AVFrame
void SetPresentationPending(bool pending)
void PresentationFinished(void)
void Destroy(void)
Clear and destroy the buffer object and its parameters.
void SetDmaBufHandle(uint32_t fd)
void SetDestroyAfterUse(bool val)
int CreatePropertyBlob(uint32_t *)
Creates a property blob.
int SetPropertyRequest(drmModeAtomicReqPtr, uint32_t, uint32_t, const char *, uint64_t)
Add a property to a request.
uint64_t DisplayHeight(void)
int HandleEvent(void)
Polls for a drm event.
uint64_t DisplayWidth(void)
uint64_t ZposPrimary(void)
uint64_t ZposOverlay(void)
cDrmPlane * PipPlane(void)
void SaveCrtc(void)
Saves information of a CRTC.
void RestoreCrtc(void)
Restore information of a CRTC.
void Close(void)
Close drm file handle.
cDrmPlane * OsdPlane(void)
cDrmPlane * VideoPlane(void)
uint32_t ConnectorId(void)
int Init(void)
Initiate the drm device.
void InitEvent(void)
Init the event context.
cDrmPlane - DRM plane class
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.
void ClearPlane(drmModeAtomicReqPtr)
Clear plane from drm.
void SetZpos(uint64_t zpos)
void FreeProperties(void)
Free the previously filled plane properties.
void SetPlane(drmModeAtomicReqPtr)
Set all other plane properties.
void SetPlaneZpos(drmModeAtomicReqPtr)
Set the plane zpos property.
void DumpParameters(const char *)
Dump the plane parameter modesetting values.
void FreeDrmBuf(void)
Free the grab buffer.
void SetDrmBuf(cDrmBuffer *)
Set the grab buffer and the dimensions how it is presented on the screen.
cDrmBuffer * GetDrmBuf(void)
void Clear(void)
Remove all elements from the queue.
T * Pop(void)
Pop an element from the back of the queue.
bool IsEmpty(void)
Check if the queue is empty.
bool IsFull(void)
Check if the queue is full.
bool Push(T *element)
Push an element to the front of the queue.
size_t Size(void)
Get the current size of the queue.
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
void SetPaused(bool)
Set audio playback paused state.
int ConfigPipAltTopPercent
0 = aligned to top, 100 = aligned to bottom
int ConfigPipLeftPercent
0 = aligned to left, 100 = aligned to right
int ConfigPipAltLeftPercent
0 = aligned to left, 100 = aligned to right
int ConfigPipAltScalePercent
alternative scale factor of pip video
int ConfigPipTopPercent
0 = aligned to top, 100 = aligned to bottom
int ConfigPipScalePercent
scale factor of pip video
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)
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.
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
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)
SoftHdDevice config header file.
State machine and event header file.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent > Event
Logger class header file.
#define LOGFATAL
Logger macros.
Misc function header file.
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Workaround for av_err2str() not working with C++.
Thread-safe queue header file.
Device class header file.
Thread classes header file.
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
#define AV_SYNC_THRESHOLD_AUDIO_AHEAD_VIDEO_MS
threshold in ms, when to drop video frames to keep audio and video in sync
Videostream class header file.