13#include "libavutil/log.h"
14#define FFNV_LOG_FUNC(logctx, msg, ...) av_log(logctx, AV_LOG_ERROR, msg, __VA_ARGS__)
15#define FFNV_DEBUG_LOG_FUNC(logctx, msg, ...) av_log(logctx, AV_LOG_DEBUG, msg, __VA_ARGS__)
16#include <ffnvcodec/dynlink_loader.h>
20#include "libavutil/hwcontext_cuda.h"
21#include "libavutil/opt.h"
24#define LOC QString("NVDEC: ")
43 const AVCodec **Codec,
48 bool decodeonly =
Decoder ==
"nvdec-dec";
60 QString codecstr = avcodec_get_name((*Context)->codec_id);
61 QString
profile = avcodec_profile_name((*Context)->codec_id, (*Context)->profile);
62 QString pixfmt = av_get_pix_fmt_name((*Context)->pix_fmt);
64 cudaVideoCodec cudacodec = cudaVideoCodec_NumCodecs;
65 switch ((*Context)->codec_id)
67 case AV_CODEC_ID_MPEG1VIDEO: cudacodec = cudaVideoCodec_MPEG1;
break;
68 case AV_CODEC_ID_MPEG2VIDEO: cudacodec = cudaVideoCodec_MPEG2;
break;
69 case AV_CODEC_ID_MPEG4: cudacodec = cudaVideoCodec_MPEG4;
break;
70 case AV_CODEC_ID_VC1: cudacodec = cudaVideoCodec_VC1;
break;
71 case AV_CODEC_ID_H264: cudacodec = cudaVideoCodec_H264;
break;
72 case AV_CODEC_ID_HEVC: cudacodec = cudaVideoCodec_HEVC;
break;
73 case AV_CODEC_ID_VP8: cudacodec = cudaVideoCodec_VP8;
break;
74 case AV_CODEC_ID_VP9: cudacodec = cudaVideoCodec_VP9;
break;
78 cudaVideoChromaFormat cudaformat = cudaVideoChromaFormat_Monochrome;
81 QString desc = QString(
"'%1 %2 %3 Depth:%4 %5x%6'")
82 .arg(codecstr,
profile, pixfmt).arg(depth + 8)
83 .arg((*Context)->width).arg((*Context)->height);
88 cudaformat = cudaVideoChromaFormat_420;
90 cudaformat = cudaVideoChromaFormat_422;
92 cudaformat = cudaVideoChromaFormat_444;
94 if ((cudacodec == cudaVideoCodec_NumCodecs) || (cudaformat == cudaVideoChromaFormat_Monochrome))
96 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
"Unknown codec or format");
97 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"NVDEC does NOT support %1").arg(desc));
104 {
return Cap.Supports(cudacodec, cudaformat, depth, (*Context)->width, (*Context)->height); };
105 if (!std::any_of(profiles.cbegin(), profiles.cend(), capcheck))
107 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
"No matching profile support");
108 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"NVDEC does NOT support %1").arg(desc));
114 QString name = QString((*Codec)->name) +
"_cuvid";
115 if (name ==
"mpeg2video_cuvid")
116 name =
"mpeg2_cuvid";
117 for (
int i = 0; ; i++)
119 const AVCodecHWConfig *config = avcodec_get_hw_config(*Codec, i);
123 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) &&
124 (config->device_type == AV_HWDEVICE_TYPE_CUDA))
126 const AVCodec *codec = avcodec_find_decoder_by_name(name.toLocal8Bit());
129 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"NVDEC supports decoding %1").arg(desc));
131 decoder->CodecMap()->FreeCodecContext(Stream);
132 *Context = decoder->CodecMap()->GetCodecContext(Stream, *Codec);
139 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to find decoder '%1'").arg(name));
164 if (!interop->IsValid())
171 AVBufferRef* hwdeviceref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_CUDA);
179 auto* hwdevicecontext =
reinterpret_cast<AVHWDeviceContext*
>(hwdeviceref->data);
181 hwdevicecontext->user_opaque = interop;
182 auto *devicehwctx =
reinterpret_cast<AVCUDADeviceContext*
>(hwdevicecontext->hwctx);
183 devicehwctx->cuda_ctx = interop->GetCUDAContext();
184 devicehwctx->stream =
nullptr;
186 if (av_hwdevice_ctx_init(hwdeviceref) < 0)
188 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to init CUDA hw device");
189 av_buffer_unref(&hwdeviceref);
193 Context->hw_device_ctx = hwdeviceref;
194 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Created CUDA device context");
203 DirectRendering =
false;
215 "Create NVDEC decoder");
223 Context->hw_device_ctx = context;
247 if (Context->height == 720)
278 deinterlacer = doubledeint;
288 if (!deinterlacer && !other && (singlepref &
DEINT_DRIVER))
289 deinterlacer = singledeint;
298 deinterlacer = doubledeint;
308 if (!deinterlacer && !other && singledeint)
311 deinterlacer = singledeint;
320 QString mode =
"adaptive";
323 int result = av_opt_set(Context->priv_data,
"deint", mode.toLocal8Bit(), 0);
326 if (av_opt_set_int(Context->priv_data,
"drop_second_field",
static_cast<int>(!DoubleRate), 0) == 0)
328 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Setup decoder deinterlacer '%1'")
341 Frame->m_interlaced =
false;
342 Frame->m_interlacedReverse =
false;
343 Frame->m_topFieldFirst =
false;
346 Frame->m_alreadyDeinterlaced =
true;
368 while (*PixFmt != AV_PIX_FMT_NONE)
370 if (*PixFmt == AV_PIX_FMT_CUDA)
375 auto * me =
dynamic_cast<MythNVDECContext*
>(decoder->GetMythCodecContext());
379 return AV_PIX_FMT_CUDA;
383 return AV_PIX_FMT_NONE;
388 if (AvFrame->format != AV_PIX_FMT_CUDA)
405 if ((AvFrame->format != AV_PIX_FMT_CUDA) || !AvFrame->data[0])
407 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Not a valid CUDA hw frame");
411 if (!Context || !
Frame)
416 for (
int i = 0; i < 3; i++)
418 Frame->m_pitches[i] = AvFrame->linesize[i];
419 Frame->m_offsets[i] = AvFrame->data[i] ? (
static_cast<int>(AvFrame->data[i] - AvFrame->data[0])) : 0;
420 Frame->m_priv[i] =
nullptr;
423 Frame->m_width = AvFrame->width;
424 Frame->m_height = AvFrame->height;
425 Frame->m_pixFmt = Context->pix_fmt;
426 Frame->m_directRendering =
true;
428 AvFrame->opaque =
Frame;
431 if (AvFrame->hw_frames_ctx)
433 auto *context =
reinterpret_cast<AVHWFramesContext*
>(AvFrame->hw_frames_ctx->data);
435 Frame->m_swPixFmt = context->sw_format;
439 Frame->m_colorshifted =
true;
442 Frame->m_buffer = AvFrame->data[0];
445 Frame->m_priv[0] =
reinterpret_cast<unsigned char*
>(av_buffer_ref(AvFrame->buf[0]));
449 Frame->m_priv[1] =
reinterpret_cast<unsigned char*
>(av_buffer_ref(Context->hw_device_ctx));
452 AvFrame->buf[1] = av_buffer_create(
reinterpret_cast<uint8_t*
>(
Frame), 0,
458 QSize Minimum, QSize Maximum,
uint MacroBlocks)
464 m_macroBlocks(MacroBlocks)
466 auto ToMythProfile = [](cudaVideoCodec CudaCodec)
483 auto ToMythFormat = [](cudaVideoChromaFormat CudaFormat)
487 case cudaVideoChromaFormat_420:
return FMT_YV12;
488 case cudaVideoChromaFormat_422:
return FMT_YUV422P;
489 case cudaVideoChromaFormat_444:
return FMT_YUV444P;
499 uint Depth,
int Width,
int Height)
const
501 uint mblocks =
static_cast<uint>((Width * Height) / 256);
503 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
504 QString(
"Trying to match: Codec %1 Format %2 Depth %3 Width %4 Height %5 MBs %6")
505 .arg(Codec).arg(
Format).arg(Depth).arg(Width).arg(Height).arg(mblocks));
506 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
507 QString(
"to this profile: Codec %1 Format %2 Depth %3 Width %4<->%5 Height %6<->%7 MBs %8")
508 .arg(m_codec).arg(m_format).arg(m_depth)
509 .arg(m_minimum.width()).arg(m_maximum.width())
510 .arg(m_minimum.height()).arg(m_maximum.height()).arg(m_macroBlocks));
512 bool result = (Codec == m_codec) && (
Format == m_format) && (Depth == m_depth) &&
513 (m_maximum.width() >= Width) && (m_maximum.height() >= Height) &&
514 (m_minimum.width() <= Width) && (m_minimum.height() <= Height) &&
515 (m_macroBlocks >= mblocks);
517 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"%1 Match").arg(result ?
"" :
"NO"));
523 static QRecursiveMutex lock;
524 QMutexLocker locker(&lock);
525 static bool s_checked =
false;
526 static bool s_available =
false;
527 if (!s_checked || Reinit)
532 if (profiles.empty())
534 LOG(VB_GENERAL, LOG_INFO,
LOC +
"No NVDEC decoders found");
539 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Supported/available NVDEC decoders:");
540 for (
const auto&
profile : profiles)
544 LOG(VB_GENERAL, LOG_INFO,
LOC + desc + QString(
" MBs: %1").arg(
profile.m_macroBlocks));
550 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"HaveNVDEC must be initialised from the main thread");
561 if (profiles.empty())
563 Decoders.append(
"NVDEC:");
564 for (
const auto&
profile : profiles)
574 static QRecursiveMutex lock;
575 static bool s_initialised =
false;
576 static std::vector<MythNVDECContext::MythNVDECCaps> s_profiles;
578 QMutexLocker locker(&lock);
581 s_initialised =
true;
584 CUcontext context =
nullptr;
585 CudaFunctions *cuda =
nullptr;
589 CuvidFunctions *cuvid =
nullptr;
590 CUcontext dummy =
nullptr;
591 cuda->cuCtxPushCurrent(context);
593 if (cuvid_load_functions(&cuvid,
nullptr) == 0)
596 if (!cuvid->cuvidGetDecoderCaps)
597 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Old driver - cannot check decoder capabilities");
600 for (
int codec = cudaVideoCodec_MPEG1; codec < cudaVideoCodec_NumCodecs; ++codec)
602 auto cudacodec =
static_cast<cudaVideoCodec
>(codec);
603 if (cudacodec == cudaVideoCodec_JPEG)
605 for (
int format = cudaVideoChromaFormat_420; format < cudaVideoChromaFormat_444; ++format)
607 auto cudaformat =
static_cast<cudaVideoChromaFormat
>(format);
608 for (
uint depth = 0; depth < 9; ++depth)
610 CUVIDDECODECAPS caps;
611 caps.eCodecType = cudacodec;
612 caps.eChromaFormat = cudaformat;
613 caps.nBitDepthMinus8 = depth;
615 if (cuvid->cuvidGetDecoderCaps && (cuvid->cuvidGetDecoderCaps(&caps) == CUDA_SUCCESS) &&
618 s_profiles.emplace_back(
619 cudacodec, depth, cudaformat,
620 QSize(caps.nMinWidth, caps.nMinHeight),
621 QSize(
static_cast<int>(caps.nMaxWidth),
static_cast<int>(caps.nMaxHeight)),
624 else if (!cuvid->cuvidGetDecoderCaps)
627 s_profiles.emplace_back(cudacodec, depth, cudaformat,
628 QSize(32, 32), QSize(8192, 8192),
629 (8192 * 8192) / 256);
634 cuvid_free_functions(&cuvid);
636 cuda->cuCtxPopCurrent(&dummy);
650 auto *frames =
reinterpret_cast<AVHWFramesContext*
>(
m_framesContext->data);
651 if ((frames->sw_format == Context->sw_pix_fmt) && (frames->width == Context->coded_width) &&
652 (frames->height == Context->coded_height))
666 AVBufferRef* framesref = av_hwframe_ctx_alloc(Context->hw_device_ctx);
667 auto *frames =
reinterpret_cast<AVHWFramesContext*
>(framesref->data);
669 frames->user_opaque =
nullptr;
670 frames->sw_format = Context->sw_pix_fmt;
671 frames->format = AV_PIX_FMT_CUDA;
672 frames->width = Context->coded_width;
673 frames->height = Context->coded_height;
674 if (av_hwframe_ctx_init(framesref) < 0)
676 av_buffer_unref(&framesref);
680 Context->hw_frames_ctx = framesref;
static VideoFrameType PixelFormatToFrameType(AVPixelFormat Fmt)
static void DeviceContextFinished(AVHWDeviceContext *Context)
static bool IsUnsupportedProfile(AVCodecContext *Context)
Most hardware decoders do not support these codecs/profiles.
static bool FrameTypeIsSupported(AVCodecContext *Context, VideoFrameType Format)
static void ReleaseBuffer(void *Opaque, uint8_t *Data)
virtual bool RetrieveHWFrame(MythVideoFrame *Frame, AVFrame *AvFrame)
static AVBufferRef * CreateDevice(AVHWDeviceType Type, MythInteropGPU *Interop, const QString &Device=QString())
static int InitialiseDecoder2(AVCodecContext *Context, CreateHWDecoder Callback, const QString &Debug)
Initialise a hardware decoder that is NOT expected to use AVHWFramesContext.
virtual void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering)
static void FramesContextFinished(AVHWFramesContext *Context)
static void NewHardwareFramesContext(void)
Track the number of concurrent frames contexts.
static QString GetProfileDescription(CodecProfile Profile, QSize Size, VideoFrameType Format=FMT_NONE, uint ColorDepth=0)
static MythPlayerUI * GetPlayerUI(AVCodecContext *Context)
QString GetSetting(const QString &key, const QString &defaultval="")
cudaVideoChromaFormat m_format
MythCodecContext::CodecProfile m_profile
MythNVDECCaps(cudaVideoCodec Codec, uint Depth, cudaVideoChromaFormat Format, QSize Minimum, QSize Maximum, uint MacroBlocks)
bool Supports(cudaVideoCodec Codec, cudaVideoChromaFormat Format, uint Depth, int Width, int Height) const
bool IsDeinterlacing(bool &DoubleRate, bool StreamChange=false) override
MythDeintType m_deinterlacer
static int InitialiseDecoder(AVCodecContext *Context)
static enum AVPixelFormat GetFormat(AVCodecContext *Context, const AVPixelFormat *PixFmt)
static void GetDecoderList(QStringList &Decoders)
static bool GetBuffer(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame, int Flags)
Convert AVFrame data to MythFrame.
void PostProcessFrame(AVCodecContext *Context, MythVideoFrame *Frame) override
static MythCodecID GetSupportedCodec(AVCodecContext **CodecContext, const AVCodec **Codec, const QString &Decoder, AVStream *Stream, uint StreamType)
Determine whether NVDEC decoding is supported for this codec.
~MythNVDECContext() override
bool RetrieveFrame(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame) override
AVBufferRef * m_framesContext
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
int HwDecoderInit(AVCodecContext *Context) override
MythNVDECContext(DecoderBase *Parent, MythCodecID CodecID)
void SetDeinterlacing(AVCodecContext *Context, MythVideoProfile *Profile, bool DoubleRate) override
Enable NVDEC/CUDA deinterlacing if necessary.
void InitFramesContext(AVCodecContext *Context)
static bool HaveNVDEC(bool Reinit=false)
bool DecoderWillResetOnFlush(void) override
static const std::vector< MythNVDECCaps > & GetProfiles(void)
static bool CreateCUDAContext(MythRenderOpenGL *GLContext, CudaFunctions *&CudaFuncs, CUcontext &CudaContext)
static MythNVDECInterop * CreateNVDEC(MythPlayerUI *Player, MythRenderOpenGL *Context)
static void CleanupContext(MythRenderOpenGL *GLContext, CudaFunctions *&CudaFuncs, CUcontext &CudaContext)
void DiscardVideoFrames(bool KeyFrame, bool Flushed)
Places frames in the available frames queue.
static MythRenderOpenGL * GetOpenGLRender(void)
static bool FormatIs422(VideoFrameType Type)
static QString DeinterlacerName(MythDeintType Deint, bool DoubleRate, VideoFrameType Format=FMT_NONE)
static bool FormatIs444(VideoFrameType Type)
static bool FormatIs420(VideoFrameType Type)
static MythDeintType ParseDeinterlacer(const QString &Deinterlacer)
static int ColorDepth(int Format)
QString GetDoubleRatePreferences() const
QString GetSingleRatePreferences() const
static bool codec_is_nvdec(MythCodecID id)
static bool codec_is_nvdec_dec(MythCodecID id)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)