Skip to content

Commit

Permalink
VDPAU: Improve decoder support check
Browse files Browse the repository at this point in the history
- statically initialise a list of supported profiles and use this to
check for decoder support when needed
- should speedup VDPAU initialisation - with the exception of H264 which
sometimes still needs a full check for certain streams.
- add VDPAU profiles to the StatusBox-Video decoders output
  • Loading branch information
mark-kendall committed Jan 29, 2020
1 parent a4d1c3f commit 5a316bc
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 128 deletions.
3 changes: 3 additions & 0 deletions mythtv/libs/libmythtv/decoders/mythcodeccontext.cpp
Expand Up @@ -115,6 +115,9 @@ QStringList MythCodecContext::GetDecoderDescription(void)
{
QStringList decoders;

#ifdef USING_VDPAU
MythVDPAUHelper::GetDecoderList(decoders);
#endif
#ifdef USING_VAAPI
MythVAAPIContext::GetDecoderList(decoders);
#endif
Expand Down
34 changes: 24 additions & 10 deletions mythtv/libs/libmythtv/decoders/mythvdpaucontext.cpp
Expand Up @@ -159,24 +159,38 @@ MythCodecID MythVDPAUContext::GetSupportedCodec(AVCodecContext **Context,
VideoFrameType type = PixelFormatToFrameType((*Context)->pix_fmt);
bool vdpau = (type == FMT_YV12) && MythVDPAUHelper::HaveVDPAU() &&
(decodeonly ? codec_is_vdpau_dechw(success) : codec_is_vdpau_hw(success));
if (vdpau && (success == kCodec_MPEG4_VDPAU || success == kCodec_MPEG4_VDPAU_DEC))
vdpau = MythVDPAUHelper::HaveMPEG4Decode();

if (vdpau)
{
MythCodecContext::CodecProfile mythprofile =
MythCodecContext::FFmpegToMythProfile((*Context)->codec_id, (*Context)->profile);
const VDPAUProfiles& profiles = MythVDPAUHelper::GetProfiles();
vdpau = false;
for (auto vdpauprofile : profiles)
{
if (vdpauprofile.first == mythprofile &&
vdpauprofile.second.Supported((*Context)->width, (*Context)->height, (*Context)->level))
{
vdpau = true;
break;
}
}
}

// H264 needs additional checks for old hardware
if (vdpau && (success == kCodec_H264_VDPAU || success == kCodec_H264_VDPAU_DEC))
vdpau = MythVDPAUHelper::CheckH264Decode(*Context);
if (vdpau && (success == kCodec_HEVC_VDPAU || success == kCodec_HEVC_VDPAU_DEC))
vdpau = MythVDPAUHelper::CheckHEVCDecode(*Context);

QString desc = QString("'%1 %2 %3 %4x%5'")
.arg(codec).arg(profile).arg(pixfmt).arg((*Context)->width).arg((*Context)->height);

if (!vdpau)
{
LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("HW device type '%1' does not support decoding '%2 %3 %4'")
.arg(av_hwdevice_get_type_name(AV_HWDEVICE_TYPE_VDPAU)).arg(codec)
.arg(profile).arg(pixfmt));
LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VDPAU does not support decoding %1").arg(desc));
return failure;
}

LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("HW device type '%1' supports decoding '%2 %3 %4'")
.arg(av_hwdevice_get_type_name(AV_HWDEVICE_TYPE_VDPAU)).arg(codec)
.arg(profile).arg(pixfmt));
LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VDPAU supports decoding %1").arg(desc));
(*Context)->pix_fmt = AV_PIX_FMT_VDPAU;
return success;
}
Expand Down
268 changes: 160 additions & 108 deletions mythtv/libs/libmythtv/decoders/mythvdpauhelper.cpp
Expand Up @@ -7,10 +7,6 @@
// Std
#include <cmath>

QMutex MythVDPAUHelper::gVDPAULock(QMutex::Recursive);
bool MythVDPAUHelper::gVDPAUAvailable = false;
bool MythVDPAUHelper::gVDPAUMPEG4Available = false;

#define LOC QString("VDPAUHelp: ")

#define INIT_ST \
Expand All @@ -29,31 +25,170 @@ if (!ok) \
#define GET_PROC(FUNC_ID, PROC) \
status = m_vdpGetProcAddress(m_device, FUNC_ID, reinterpret_cast<void **>(&(PROC))); CHECK_ST

VDPAUCodec::VDPAUCodec(MythCodecContext::CodecProfile Profile, QSize Size, uint32_t Macroblocks, uint32_t Level)
: m_maxSize(Size),
m_maxMacroBlocks(Macroblocks),
m_maxLevel(Level)
{
// Levels don't work for MPEG1/2
if (MythCodecContext::MPEG1 <= Profile && Profile <= MythCodecContext::MPEG2SNR)
m_maxLevel = 1000;
}

bool VDPAUCodec::Supported(int Width, int Height, int Level)
{
uint32_t macros = static_cast<uint32_t>(((Width + 15) & ~15) * ((Height + 15) & ~15)) / 256;
return (Width <= m_maxSize.width()) && (Height <= m_maxSize.height()) &&
(macros <= m_maxMacroBlocks) && (static_cast<uint32_t>(Level) <= m_maxLevel);
}

bool MythVDPAUHelper::HaveVDPAU(void)
{
QMutexLocker locker(&gVDPAULock);
static QMutex s_mutex;
static bool s_checked = false;
static bool s_available = false;

QMutexLocker locker(&s_mutex);
if (s_checked)
return gVDPAUAvailable;
return s_available;

{
MythVDPAUHelper vdpau;
s_available = vdpau.IsValid();
}

MythVDPAUHelper vdpau;
gVDPAUAvailable = vdpau.IsValid();
s_checked = true;
if (gVDPAUAvailable)
if (s_available)
{
LOG(VB_GENERAL, LOG_INFO, LOC + "VDPAU is available");
gVDPAUMPEG4Available = vdpau.CheckMPEG4();
LOG(VB_GENERAL, LOG_INFO, LOC + "Supported/available VDPAU decoders:");
const VDPAUProfiles& profiles = MythVDPAUHelper::GetProfiles();
foreach (auto profile, profiles)
LOG(VB_GENERAL, LOG_INFO, LOC +
MythCodecContext::GetProfileDescription(profile.first, profile.second.m_maxSize));
}
else
{
LOG(VB_GENERAL, LOG_INFO, LOC + "VDPAU is NOT available");
}
return gVDPAUAvailable;
return s_available;
}

bool MythVDPAUHelper::ProfileCheck(VdpDecoderProfile Profile, uint32_t &Level,
uint32_t &Macros, uint32_t &Width, uint32_t &Height)
{
if (!m_device)
return false;

INIT_ST
VdpBool supported = 0;
status = m_vdpDecoderQueryCapabilities(m_device, Profile, &supported,
&Level, &Macros, &Width, &Height);
CHECK_ST
return supported > 0;
}

const VDPAUProfiles& MythVDPAUHelper::GetProfiles(void)
{
static const VdpDecoderProfile MainProfiles[] =
{
VDP_DECODER_PROFILE_MPEG1, VDP_DECODER_PROFILE_MPEG2_SIMPLE, VDP_DECODER_PROFILE_MPEG2_MAIN,
VDP_DECODER_PROFILE_MPEG4_PART2_SP, VDP_DECODER_PROFILE_MPEG4_PART2_ASP,
VDP_DECODER_PROFILE_VC1_SIMPLE, VDP_DECODER_PROFILE_VC1_MAIN, VDP_DECODER_PROFILE_VC1_ADVANCED,
VDP_DECODER_PROFILE_H264_BASELINE, VDP_DECODER_PROFILE_H264_MAIN, VDP_DECODER_PROFILE_H264_HIGH,
VDP_DECODER_PROFILE_H264_EXTENDED, VDP_DECODER_PROFILE_H264_CONSTRAINED_BASELINE,
VDP_DECODER_PROFILE_H264_CONSTRAINED_HIGH, VDP_DECODER_PROFILE_H264_HIGH_444_PREDICTIVE
};

static const VdpDecoderProfile HEVCProfiles[] =
{
VDP_DECODER_PROFILE_HEVC_MAIN, VDP_DECODER_PROFILE_HEVC_MAIN_10,
VDP_DECODER_PROFILE_HEVC_MAIN_STILL, VDP_DECODER_PROFILE_HEVC_MAIN_444
};

auto VDPAUToMythProfile = [](VdpDecoderProfile Profile)
{
switch (Profile)
{
case VDP_DECODER_PROFILE_MPEG1: return MythCodecContext::MPEG1;
case VDP_DECODER_PROFILE_MPEG2_SIMPLE: return MythCodecContext::MPEG2Simple;
case VDP_DECODER_PROFILE_MPEG2_MAIN: return MythCodecContext::MPEG2Main;

case VDP_DECODER_PROFILE_MPEG4_PART2_SP: return MythCodecContext::MPEG4Simple;
case VDP_DECODER_PROFILE_MPEG4_PART2_ASP: return MythCodecContext::MPEG4AdvancedSimple;

case VDP_DECODER_PROFILE_VC1_SIMPLE: return MythCodecContext::VC1Simple;
case VDP_DECODER_PROFILE_VC1_MAIN: return MythCodecContext::VC1Main;
case VDP_DECODER_PROFILE_VC1_ADVANCED: return MythCodecContext::VC1Advanced;

case VDP_DECODER_PROFILE_H264_BASELINE: return MythCodecContext::H264Baseline;
case VDP_DECODER_PROFILE_H264_MAIN: return MythCodecContext::H264Main;
case VDP_DECODER_PROFILE_H264_HIGH: return MythCodecContext::H264High;
case VDP_DECODER_PROFILE_H264_EXTENDED: return MythCodecContext::H264Extended;
case VDP_DECODER_PROFILE_H264_CONSTRAINED_BASELINE: return MythCodecContext::H264ConstrainedBaseline;
case VDP_DECODER_PROFILE_H264_CONSTRAINED_HIGH: return MythCodecContext::H264ConstrainedHigh;
case VDP_DECODER_PROFILE_H264_HIGH_444_PREDICTIVE: return MythCodecContext::H264High444; // ?

case VDP_DECODER_PROFILE_HEVC_MAIN: return MythCodecContext::HEVCMain;
case VDP_DECODER_PROFILE_HEVC_MAIN_10: return MythCodecContext::HEVCMain10;
case VDP_DECODER_PROFILE_HEVC_MAIN_STILL: return MythCodecContext::HEVCMainStill;
case VDP_DECODER_PROFILE_HEVC_MAIN_444: return MythCodecContext::HEVCRext;
}
return MythCodecContext::NoProfile;
};

static QMutex lock(QMutex::Recursive);
static bool s_initialised = false;
static VDPAUProfiles s_profiles;

QMutexLocker locker(&lock);
if (s_initialised)
return s_profiles;
s_initialised = true;

MythVDPAUHelper helper;
if (!helper.IsValid())
return s_profiles;

uint32_t level = 0;
uint32_t macros = 0;
uint32_t width = 0;
uint32_t height = 0;
for (VdpDecoderProfile profile : MainProfiles)
{
if (helper.ProfileCheck(profile, level, macros, width, height))
{
MythCodecContext::CodecProfile prof = VDPAUToMythProfile(profile);
s_profiles.append(VDPAUProfile(prof,
VDPAUCodec(prof, QSize(static_cast<int>(width), static_cast<int>(height)), macros, level)));
}
}

if (helper.HEVCSupported())
{
for (VdpDecoderProfile profile : HEVCProfiles)
{
if (helper.ProfileCheck(profile, level, macros, width, height))
{
MythCodecContext::CodecProfile prof = VDPAUToMythProfile(profile);
s_profiles.append(VDPAUProfile(prof,
VDPAUCodec(prof, QSize(static_cast<int>(width), static_cast<int>(height)), macros, level)));
}
}
}

return s_profiles;
}

bool MythVDPAUHelper::HaveMPEG4Decode(void)
void MythVDPAUHelper::GetDecoderList(QStringList &Decoders)
{
return gVDPAUMPEG4Available;
const VDPAUProfiles& profiles = MythVDPAUHelper::GetProfiles();
if (profiles.isEmpty())
return;

Decoders.append("VDPAU:");
foreach (auto profile, profiles)
if (profile.first != MythCodecContext::MJPEG)
Decoders.append(MythCodecContext::GetProfileDescription(profile.first, profile.second.m_maxSize));
}

static void vdpau_preemption_callback(VdpDevice /*unused*/, void* Opaque)
Expand Down Expand Up @@ -155,44 +290,11 @@ void MythVDPAUHelper::SetPreempted(void)
emit DisplayPreempted();
}

bool MythVDPAUHelper::CheckMPEG4(void)
bool MythVDPAUHelper::HEVCSupported(void)
{
if (!m_valid)
return false;

#ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
INIT_ST
VdpBool supported = false;
uint32_t tmp1 = 0;
uint32_t tmp2 = 0;
uint32_t tmp3 = 0;
uint32_t tmp4 = 0;
status = m_vdpDecoderQueryCapabilities(m_device,
VDP_DECODER_PROFILE_MPEG4_PART2_ASP, &supported,
&tmp1, &tmp2, &tmp3, &tmp4);
CHECK_ST
return supported;
#else
return false;
#endif
}

bool MythVDPAUHelper::CheckHEVCDecode(AVCodecContext *Context)
{
if (!Context)
return false;

MythVDPAUHelper vdpau;
if (vdpau.IsValid())
return vdpau.HEVCProfileCheck(Context);
return false;
}

bool MythVDPAUHelper::HEVCProfileCheck(AVCodecContext *Context)
{
if (!m_valid || !Context)
return false;

// FFmpeg will disallow HEVC VDPAU for driver versions < 410
const char* infostring = nullptr;
INIT_ST
Expand All @@ -203,44 +305,21 @@ bool MythVDPAUHelper::HEVCProfileCheck(AVCodecContext *Context)

int driver = 0;
sscanf(infostring, "NVIDIA VDPAU Driver Shared Library %d", &driver);
if (driver < 410)
return false;

VdpDecoderProfile profile = 0;
switch (Context->profile)
{
#ifdef VDP_DECODER_PROFILE_HEVC_MAIN
case FF_PROFILE_HEVC_MAIN: profile = VDP_DECODER_PROFILE_HEVC_MAIN; break;
#endif
#ifdef VDP_DECODER_PROFILE_HEVC_MAIN_10
case FF_PROFILE_HEVC_MAIN_10: profile = VDP_DECODER_PROFILE_HEVC_MAIN_10; break;
#endif
#ifdef VDP_DECODER_PROFILE_HEVC_MAIN_STILL
case FF_PROFILE_HEVC_MAIN_STILL_PICTURE: profile = VDP_DECODER_PROFILE_HEVC_MAIN_STILL; break;
#endif
default: return false;
}

VdpBool supported = false;
uint32_t level = 0;
uint32_t macros = 0;
uint32_t width = 0;
uint32_t height = 0;
status = m_vdpDecoderQueryCapabilities(m_device, profile, &supported, &level, &macros, &width, &height);
CHECK_ST
if (!supported)
return false;

return (width >= static_cast<uint>(Context->width)) &&
(height >= static_cast<uint>(Context->height)) &&
(level >= static_cast<uint>(Context->level));
return !(driver < 410);
}

bool MythVDPAUHelper::CheckH264Decode(AVCodecContext *Context)
{
if (!Context)
return false;

int mbs = static_cast<int>(ceil(static_cast<double>(Context->width) / 16.0));
if (!(mbs == 49 ) || (mbs == 54 ) || (mbs == 59 ) || (mbs == 64) ||
(mbs == 113) || (mbs == 118) || (mbs == 123) || (mbs == 128))
{
return true;
}

VdpDecoderProfile profile = 0;
switch (Context->profile & ~FF_PROFILE_H264_INTRA)
{
Expand All @@ -262,17 +341,11 @@ bool MythVDPAUHelper::CheckH264Decode(AVCodecContext *Context)
default: return false;
}

int mbs = static_cast<int>(ceil(static_cast<double>(Context->width) / 16.0));
int check = (mbs == 49 ) || (mbs == 54 ) || (mbs == 59 ) || (mbs == 64) ||
(mbs == 113) || (mbs == 118) || (mbs == 123) || (mbs == 128);

// Create an instance
MythVDPAUHelper helper;
if (!helper.IsValid())
return false;
if (check)
if (helper.IsValid())
return helper.H264DecodeCheck(profile, Context);
return helper.H264ProfileCheck(profile, Context);
return false;
}

bool MythVDPAUHelper::H264DecodeCheck(VdpDecoderProfile Profile, AVCodecContext *Context)
Expand All @@ -295,27 +368,6 @@ bool MythVDPAUHelper::H264DecodeCheck(VdpDecoderProfile Profile, AVCodecContext
return ok;
}

bool MythVDPAUHelper::H264ProfileCheck(VdpDecoderProfile Profile, AVCodecContext *Context)
{
if (!m_valid || !Context)
return false;

VdpBool supported = false;
uint32_t level = 0;
uint32_t macros = 0;
uint32_t width = 0;
uint32_t height = 0;
INIT_ST
status = m_vdpDecoderQueryCapabilities(m_device, Profile, &supported, &level, &macros, &width, &height);
CHECK_ST
if (!supported)
return false;

return (width >= static_cast<uint>(Context->width)) &&
(height >= static_cast<uint>(Context->height)) &&
(level >= static_cast<uint>(Context->level));
}

VdpOutputSurface MythVDPAUHelper::CreateOutputSurface(QSize Size)
{
if (!m_valid)
Expand Down

0 comments on commit 5a316bc

Please sign in to comment.