68 changes: 68 additions & 0 deletions mythtv/libs/libmythtv/drm/mythvideodrmutils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// MythTV
#include "mythlogging.h"
#include "platforms/drm/mythdrmproperty.h"
#include "drm/mythvideodrmutils.h"

// libavutil
extern "C" {
#include "libavutil/pixfmt.h"
#include "libavutil/pixdesc.h"
}

#define LOC QString("DRMUtils: ")

uint64_t MythVideoDRMUtils::FFmpegColorRangeToDRM(DRMProp Property, int Range)
{
// Default to limited range
uint64_t result = 0;

// Pull out enums
auto rangeprop = dynamic_cast<MythDRMEnumProperty*>(Property.get());
if (!rangeprop)
return result;

auto searchstring = (Range == AVCOL_RANGE_MPEG) ? "limited" : "full";
for (const auto & [value,name] : rangeprop->m_enums)
{
if (name.contains(searchstring, Qt::CaseInsensitive))
{
LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Using '%1' as color range for '%2'")
.arg(name).arg(av_color_range_name(static_cast<AVColorRange>(Range))));
return value;
}
}
return result;
}

uint64_t MythVideoDRMUtils::FFmpegColorEncodingToDRM(DRMProp Property, int Encoding)
{
// Default to BT.601
uint64_t result = 0;

// Pull out enums
auto rangeprop = dynamic_cast<MythDRMEnumProperty*>(Property.get());
if (!rangeprop)
return result;

auto searchstring = "601";
switch (Encoding)
{
case AVCOL_SPC_BT709: searchstring = "709"; break;
case AVCOL_SPC_BT2020_NCL:
case AVCOL_SPC_BT2020_CL:
case AVCOL_SPC_CHROMA_DERIVED_NCL:
case AVCOL_SPC_CHROMA_DERIVED_CL: searchstring = "2020"; break;
default: break;
}

for (const auto & [value,name] : rangeprop->m_enums)
{
if (name.contains(searchstring, Qt::CaseInsensitive))
{
LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Using '%1' as color encoding for '%2'")
.arg(name).arg(av_color_space_name(static_cast<AVColorSpace>(Encoding))));
return value;
}
}
return result;
}
14 changes: 14 additions & 0 deletions mythtv/libs/libmythtv/drm/mythvideodrmutils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef MYTHVIDEODRMUTILS_H
#define MYTHVIDEODRMUTILS_H

// MythTV
#include "platforms/drm/mythdrmproperty.h"

class MythVideoDRMUtils
{
public:
static uint64_t FFmpegColorRangeToDRM (DRMProp Property, int Range);
static uint64_t FFmpegColorEncodingToDRM (DRMProp Property, int Encoding);
};

#endif
16 changes: 15 additions & 1 deletion mythtv/libs/libmythtv/libmythtv.pro
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ DEPENDPATH += ../libmyth ../libmyth/audio
DEPENDPATH += ../libmythbase
DEPENDPATH += ./mpeg ./channelscan ./mheg ./decoders ./opengl ./io ./captions
DEPENDPATH += ./visualisations ./visualisations/opengl ./visualisations/vulkan
DEPENDPATH += ./vulkan
DEPENDPATH += ./vulkan ./drm
DEPENDPATH += ./overlays
DEPENDPATH += ./recorders
DEPENDPATH += ./recorders/dvbdev
Expand Down Expand Up @@ -521,6 +521,20 @@ using_frontend {
LIBS += -lvdpau
}

using_drm:using_qtprivateheaders {
DEFINES += USING_DRM
DEFINES += USING_DRM_VIDEO
DEFINES += USING_QTPRIVATEHEADERS
QT += gui-private
QMAKE_CXXFLAGS += $${LIBDRM_CFLAGS}
HEADERS += drm/mythvideodrm.h
HEADERS += drm/mythvideodrmbuffer.h
HEADERS += drm/mythvideodrmutils.h
SOURCES += drm/mythvideodrm.cpp
SOURCES += drm/mythvideodrmbuffer.cpp
SOURCES += drm/mythvideodrmutils.cpp
}

using_vaapi {
DEFINES += USING_VAAPI
HEADERS += decoders/mythvaapicontext.h
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/mythframe.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ class MTV_PUBLIC MythVideoFrame

// Presentation details for 'pure' direct rendering methods (e.g. DRM)
// Experimental and may be removed.
bool m_displayed { false };
QRect m_srcRect;
QRect m_dstRect;

Expand Down
74 changes: 66 additions & 8 deletions mythtv/libs/libmythtv/opengl/mythdrmprimeinterop.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
// MythTV
#include "mythplayerui.h"
#include "mythvideocolourspace.h"
#include "mythdrmprimeinterop.h"

#ifdef USING_DRM_VIDEO
#include "mythmainwindow.h"
#include "platforms/mythdisplaydrm.h"
#endif

// FFmpeg
extern "C" {
#include "libavutil/hwcontext_drm.h"
Expand All @@ -10,14 +16,17 @@ extern "C" {

#define LOC QString("DRMInterop: ")

MythDRMPRIMEInterop::MythDRMPRIMEInterop(MythRenderOpenGL* Context, MythPlayerUI* Player)
: MythOpenGLInterop(Context, GL_DRMPRIME, Player),
MythDRMPRIMEInterop::MythDRMPRIMEInterop(MythRenderOpenGL* Context, MythPlayerUI* Player, InteropType Type)
: MythOpenGLInterop(Context, Type, Player),
MythEGLDMABUF(Context)
{
}

MythDRMPRIMEInterop::~MythDRMPRIMEInterop()
{
#ifdef USING_DRM_VIDEO
delete m_drm;
#endif
MythDRMPRIMEInterop::DeleteTextures();
}

Expand Down Expand Up @@ -49,19 +58,35 @@ void MythDRMPRIMEInterop::DeleteTextures(void)
}

/*! \brief Create a DRM PRIME interop instance.
*
* \note This is called directly from the decoder - hence we do not attempt
* to retrieve the list of supported types again. Assume it has already been verified.
*/
MythDRMPRIMEInterop* MythDRMPRIMEInterop::CreateDRM(MythRenderOpenGL* Context, MythPlayerUI* Player)
{
return Context ? new MythDRMPRIMEInterop(Context, Player) : nullptr;
if (!(Player && Context))
return nullptr;

const auto & types = Player->GetInteropTypes();
if (const auto & drm = types.find(FMT_DRMPRIME); drm != types.cend())
for (auto type : drm->second)
if ((type == GL_DRMPRIME) || (type == DRM_DRMPRIME))
return new MythDRMPRIMEInterop(Context, Player, type);

return nullptr;
}

void MythDRMPRIMEInterop::GetDRMTypes(MythRenderOpenGL* Render, MythInteropGPU::InteropMap& Types)
{
MythInteropGPU::InteropTypes drmtypes;

#ifdef USING_DRM_VIDEO
if (MythDisplayDRM::DirectRenderingAvailable())
drmtypes.emplace_back(DRM_DRMPRIME);
#endif

if (HaveDMABuf(Render))
Types[FMT_DRMPRIME] = { GL_DRMPRIME };
drmtypes.emplace_back(GL_DRMPRIME);

if (!drmtypes.empty())
Types[FMT_DRMPRIME] = drmtypes;
}

AVDRMFrameDescriptor* MythDRMPRIMEInterop::VerifyBuffer(MythRenderOpenGL *Context, MythVideoFrame *Frame)
Expand Down Expand Up @@ -106,10 +131,15 @@ vector<MythVideoTextureOpenGL*> MythDRMPRIMEInterop::Acquire(MythRenderOpenGL *C
if (!Frame)
return result;

auto *drmdesc = VerifyBuffer(Context, Frame);
auto * drmdesc = VerifyBuffer(Context, Frame);
if (!drmdesc)
return result;

#ifdef USING_DRM_VIDEO
if (HandleDRMVideo(ColourSpace, Frame, drmdesc))
return result;
#endif

bool firstpass = m_openglTextures.isEmpty();
bool interlaced = is_interlaced(Scan);
bool composed = static_cast<uint>(drmdesc->nb_layers) == 1 && m_composable;
Expand Down Expand Up @@ -205,3 +235,31 @@ vector<MythVideoTextureOpenGL*> MythDRMPRIMEInterop::Acquire(MythRenderOpenGL *C
return result;
}

#ifdef USING_DRM_VIDEO
bool MythDRMPRIMEInterop::HandleDRMVideo(MythVideoColourSpace* ColourSpace, MythVideoFrame* Frame,
AVDRMFrameDescriptor* DRMDesc)
{
if (!((m_type == DRM_DRMPRIME) && !m_drmTriedAndFailed && Frame && DRMDesc && ColourSpace))
return false;

if (!m_drm)
m_drm = new MythVideoDRM(ColourSpace);

if (m_drm)
{
if (m_drm->IsValid())
if (m_drm->RenderFrame(DRMDesc, Frame))
return true;

// RenderFrame may have decided we should give up
if (!m_drm->IsValid())
{
LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling DRM video");
m_drmTriedAndFailed = true;
delete m_drm;
m_drm = nullptr;
}
}
return false;
}
#endif
15 changes: 13 additions & 2 deletions mythtv/libs/libmythtv/opengl/mythdrmprimeinterop.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,37 @@
#include "mythegldmabuf.h"
#include "mythopenglinterop.h"

#ifdef USING_DRM_VIDEO
#include "drm/mythvideodrm.h"
#endif

struct AVDRMFrameDescriptor;

class MythDRMPRIMEInterop : public MythOpenGLInterop, public MythEGLDMABUF
{
public:
static void GetDRMTypes(MythRenderOpenGL* Render, MythInteropGPU::InteropMap& Types);
static MythDRMPRIMEInterop* CreateDRM(MythRenderOpenGL* Context, MythPlayerUI* Player = nullptr);
static MythDRMPRIMEInterop* CreateDRM(MythRenderOpenGL* Context, MythPlayerUI* Player);
void DeleteTextures(void) override;
vector<MythVideoTextureOpenGL*> Acquire(MythRenderOpenGL *Context,
MythVideoColourSpace *ColourSpace,
MythVideoFrame *Frame, FrameScanType Scan) override;

protected:
MythDRMPRIMEInterop(MythRenderOpenGL* Context, MythPlayerUI* Player);
MythDRMPRIMEInterop(MythRenderOpenGL* Context, MythPlayerUI* Player, InteropType Type);
~MythDRMPRIMEInterop() override;

private:
AVDRMFrameDescriptor* VerifyBuffer(MythRenderOpenGL *Context, MythVideoFrame *Frame);
bool m_deinterlacing { false };
bool m_composable { true };

#ifdef USING_DRM_VIDEO
bool HandleDRMVideo(MythVideoColourSpace* ColourSpace, MythVideoFrame* Frame,
AVDRMFrameDescriptor* DRMDesc);
MythVideoDRM* m_drm { nullptr };
bool m_drmTriedAndFailed { false };
#endif
};

#endif
28 changes: 16 additions & 12 deletions mythtv/libs/libmythtv/opengl/mythopenglvideo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,24 +671,28 @@ void MythOpenGLVideo::RenderFrame(MythVideoFrame* Frame, bool TopFieldFirst, Fra
// nothing to display, then fallback to this framebuffer.
// N.B. this is now strictly necessary with v4l2 and DRM PRIME direct rendering
// but ignore now for performance reasons
bool expectinghwframes = Frame ? MythVideoFrame::HardwareFramesFormat(Frame->m_type) :
MythVideoFrame::HardwareFramesFormat(m_inputType);
VideoResizing resize = expectinghwframes ? Framebuffer: None;

// This is experimental support for direct rendering to a framebuffer (e.g. DRM).
// It may be removed or refactored (e.g. pass presentation details through to
// the interop).
if (Frame && expectinghwframes)
{
Frame->m_srcRect = m_videoRect;
Frame->m_dstRect = m_displayVideoRect;
}
VideoResizing resize = Frame ? (MythVideoFrame::HardwareFramesFormat(Frame->m_type) ? Framebuffer : None) :
(MythVideoFrame::HardwareFramesFormat(m_inputType) ? Framebuffer : None);

vector<MythVideoTextureOpenGL*> inputtextures = m_inputTextures;
if (inputtextures.empty())
{
// This is experimental support for direct rendering to a framebuffer (e.g. DRM).
// It may be removed or refactored (e.g. pass presentation details through to
// the interop).
if (Frame)
{
Frame->m_displayed = false;
Frame->m_srcRect = m_videoRect;
Frame->m_dstRect = m_displayVideoRect;
}

// Pull in any hardware frames
inputtextures = MythOpenGLInterop::Retrieve(m_openglRender, m_videoColourSpace, Frame, Scan);

if (Frame && Frame->m_displayed)
return;

if (!inputtextures.empty())
{
hwframes = true;
Expand Down
45 changes: 43 additions & 2 deletions mythtv/libs/libmythtv/opengl/mythvaapidrminterop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ extern "C" {

#define LOC QString("VAAPIDRM: ")

MythVAAPIInteropDRM::MythVAAPIInteropDRM(MythPlayerUI *Player, MythRenderOpenGL* Context)
: MythVAAPIInterop(Player, Context, GL_VAAPIEGLDRM),
MythVAAPIInteropDRM::MythVAAPIInteropDRM(MythPlayerUI *Player, MythRenderOpenGL* Context, InteropType Type)
: MythVAAPIInterop(Player, Context, Type),
MythEGLDMABUF(Context)
{
QString device = gCoreContext->GetSetting("VAAPIDevice");
Expand Down Expand Up @@ -48,6 +48,9 @@ MythVAAPIInteropDRM::MythVAAPIInteropDRM(MythPlayerUI *Player, MythRenderOpenGL*

MythVAAPIInteropDRM::~MythVAAPIInteropDRM()
{
#ifdef USING_DRM_VIDEO
delete m_drm;
#endif
OpenGLLocker locker(m_openglContext);
CleanupDRMPRIME();
CleanupReferenceFrames();
Expand Down Expand Up @@ -248,6 +251,12 @@ vector<MythVideoTextureOpenGL*> MythVAAPIInteropDRM::Acquire(MythRenderOpenGL* C
}
m_discontinuityCounter = Frame->m_frameCounter;

#ifdef USING_DRM_VIDEO
if (!m_drmTriedAndFailed)
if (HandleDRMVideo(ColourSpace, id, Frame))
return result;
#endif

// return cached texture if available
if (m_openglTextures.contains(id))
{
Expand Down Expand Up @@ -506,3 +515,35 @@ bool MythVAAPIInteropDRM::TestPrimeInterop()
return false;
#endif
}

#ifdef USING_DRM_VIDEO
bool MythVAAPIInteropDRM::HandleDRMVideo(MythVideoColourSpace* ColourSpace, VASurfaceID Id, MythVideoFrame* Frame)
{
if (!((m_type == DRM_DRMPRIME) && m_usePrime && Id && Frame && ColourSpace))
return false;

if (!m_drm)
m_drm = new MythVideoDRM(ColourSpace);

if (m_drm)
{
if (m_drm->IsValid())
{
if (!m_drmFrames.contains(Id))
m_drmFrames.insert(Id, GetDRMFrameDescriptor(Id));
if (m_drm->RenderFrame(m_drmFrames[Id], Frame))
return true;
}

// RenderFrame may have decided we should give up
if (!m_drm->IsValid())
{
LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling DRM video");
m_drmTriedAndFailed = true;
delete m_drm;
m_drm = nullptr;
}
}
return false;
}
#endif
12 changes: 11 additions & 1 deletion mythtv/libs/libmythtv/opengl/mythvaapidrminterop.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
#include "opengl/mythegldmabuf.h"
#include "opengl/mythvaapiinterop.h"

#ifdef USING_DRM_VIDEO
#include "drm/mythvideodrm.h"
#endif

class MythDRMPRIMEInterop;
struct AVDRMFrameDescriptor;

class MythVAAPIInteropDRM : public MythVAAPIInterop, public MythEGLDMABUF
{
public:
MythVAAPIInteropDRM(MythPlayerUI* Player, MythRenderOpenGL* Context);
MythVAAPIInteropDRM(MythPlayerUI* Player, MythRenderOpenGL* Context, InteropType Type);
~MythVAAPIInteropDRM() override;
vector<MythVideoTextureOpenGL*> Acquire(MythRenderOpenGL* Context,
MythVideoColourSpace* ColourSpace,
Expand Down Expand Up @@ -42,6 +46,12 @@ class MythVAAPIInteropDRM : public MythVAAPIInterop, public MythEGLDMABUF
bool TestPrimeInterop();
bool m_usePrime { false };
QHash<unsigned long long, AVDRMFrameDescriptor*> m_drmFrames { };

#ifdef USING_DRM_VIDEO
bool HandleDRMVideo(MythVideoColourSpace* ColourSpace, VASurfaceID Id, MythVideoFrame* Frame);
MythVideoDRM* m_drm { nullptr };
bool m_drmTriedAndFailed { false };
#endif
};

#endif
20 changes: 15 additions & 5 deletions mythtv/libs/libmythtv/opengl/mythvaapiinterop.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// MythTV
#ifdef USING_DRM_VIDEO
#include "platforms/mythdisplaydrm.h"
#endif

#include "mythvideoout.h"
#include "mythplayerui.h"
#include "mythvideocolourspace.h"
#include "fourcc.h"
#include "mythvaapiinterop.h"
Expand Down Expand Up @@ -42,6 +47,12 @@ void MythVAAPIInterop::GetVAAPITypes(MythRenderOpenGL* Context, MythInteropGPU::

// best first
MythInteropGPU::InteropTypes vaapitypes;

#ifdef USING_DRM_VIDEO
if (MythDisplayDRM::DirectRenderingAvailable())
vaapitypes.emplace_back(DRM_DRMPRIME);
#endif

#ifdef USING_EGL
// zero copy
if (egl && MythVAAPIInteropDRM::IsSupported(Context))
Expand All @@ -63,15 +74,14 @@ MythVAAPIInterop* MythVAAPIInterop::CreateVAAPI(MythPlayerUI *Player, MythRender
if (!(Player && Context))
return nullptr;

MythInteropGPU::InteropMap types;
MythVAAPIInterop::GetVAAPITypes(Context, types);
if (auto vaapi = types.find(FMT_VAAPI); vaapi != types.end())
const auto & types = Player->GetInteropTypes();
if (const auto & vaapi = types.find(FMT_VAAPI); vaapi != types.cend())
{
for (auto type : vaapi->second)
{
#ifdef USING_EGL
if (type == GL_VAAPIEGLDRM)
return new MythVAAPIInteropDRM(Player, Context);
if ((type == GL_VAAPIEGLDRM) || (type == DRM_DRMPRIME))
return new MythVAAPIInteropDRM(Player, Context, type);
#endif
if (type == GL_VAAPIGLXPIX)
return new MythVAAPIInteropGLXPixmap(Player, Context);
Expand Down