5#include <QCoreApplication>
6#include <QWaitCondition>
21#include "libavcodec/defs.h"
22#include "libavutil/hwcontext_vaapi.h"
23#include "libavutil/pixdesc.h"
24#include "libavfilter/buffersink.h"
27#define LOC QString("VAAPIDec: ")
52 switch (Codec->codec_id)
54 case AV_CODEC_ID_MPEG2VIDEO:
55 switch (Codec->profile)
57 case AV_PROFILE_MPEG2_SIMPLE:
return VAProfileMPEG2Simple;
58 case AV_PROFILE_MPEG2_MAIN:
return VAProfileMPEG2Main;
62 case AV_CODEC_ID_H263:
return VAProfileH263Baseline;
63 case AV_CODEC_ID_MPEG4:
64 switch (Codec->profile)
66 case AV_PROFILE_MPEG4_SIMPLE:
return VAProfileMPEG4Simple;
67 case AV_PROFILE_MPEG4_ADVANCED_SIMPLE:
return VAProfileMPEG4AdvancedSimple;
68 case AV_PROFILE_MPEG4_MAIN:
return VAProfileMPEG4Main;
72 case AV_CODEC_ID_H264:
73 switch (Codec->profile)
75 case AV_PROFILE_H264_CONSTRAINED_BASELINE:
return VAProfileH264ConstrainedBaseline;
76 case AV_PROFILE_H264_MAIN:
return VAProfileH264Main;
77 case AV_PROFILE_H264_HIGH:
return VAProfileH264High;
81 case AV_CODEC_ID_HEVC:
82 switch (Codec->profile)
84 case AV_PROFILE_HEVC_MAIN:
return VAProfileHEVCMain;
85 case AV_PROFILE_HEVC_MAIN_10:
return VAProfileHEVCMain10;
89 case AV_CODEC_ID_MJPEG:
return VAProfileJPEGBaseline;
90 case AV_CODEC_ID_WMV3:
92 switch (Codec->profile)
94 case AV_PROFILE_VC1_SIMPLE:
return VAProfileVC1Simple;
95 case AV_PROFILE_VC1_MAIN:
return VAProfileVC1Main;
96 case AV_PROFILE_VC1_ADVANCED:
97 case AV_PROFILE_VC1_COMPLEX:
return VAProfileVC1Advanced;
101 case AV_CODEC_ID_VP8:
return VAProfileVP8Version0_3;
102 case AV_CODEC_ID_VP9:
103 switch (Codec->profile)
105 case AV_PROFILE_VP9_0:
return VAProfileVP9Profile0;
106 case AV_PROFILE_VP9_2:
return VAProfileVP9Profile2;
113 return VAProfileNone;
120 case AV_PIX_FMT_YUV420P10:
return AV_PIX_FMT_P010;
121 case AV_PIX_FMT_YUV420P12:
122 case AV_PIX_FMT_YUV420P14:
123 case AV_PIX_FMT_YUV420P16:
return AV_PIX_FMT_P016;
124 default:
return AV_PIX_FMT_NV12;
135 bool decodeonly =
Decoder ==
"vaapi-dec";
139 if (!
Decoder.startsWith(
"vaapi") || vendor.isEmpty() || qEnvironmentVariableIsSet(
"NO_VAAPI"))
142 const auto * codec = avcodec_get_name((*Context)->codec_id);
143 const auto *
profile = avcodec_profile_name((*Context)->codec_id, (*Context)->profile);
144 const auto * pixfmt = av_get_pix_fmt_name((*Context)->pix_fmt);
148 if (desired == VAProfileNone)
150 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI does not support decoding '%1 %2 %3'")
159 if (vendor.contains(
"ironlake", Qt::CaseInsensitive))
161 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Disallowing VAAPI decode only for Ironlake");
177 return std::ranges::any_of(std::as_const(profiles),
178 [&Profile,Size](
auto vaprofile)
179 {
return vaprofile.first == Profile &&
180 vaprofile.second.first.width() <= Size.width() &&
181 vaprofile.second.first.height() <= Size.height() &&
182 vaprofile.second.second.width() >= Size.width() &&
183 vaprofile.second.second.height() >= Size.height(); } );
186 ok = haveprofile(mythprofile, QSize((*Context)->width, (*Context)->height));
188 if (ok && (AV_PIX_FMT_YUVJ420P == (*Context)->pix_fmt || AV_PIX_FMT_YUVJ422P == (*Context)->pix_fmt ||
189 AV_PIX_FMT_YUVJ444P == (*Context)->pix_fmt))
194 auto desc = QString(
"'%1 %2 %3 %4x%5'").arg(codec,
profile, pixfmt)
195 .arg((*Context)->width).arg((*Context)->height);
199 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI supports decoding %1").arg(desc));
200 (*Context)->pix_fmt = AV_PIX_FMT_VAAPI;
204 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI does NOT support %1").arg(desc));
210 while (*PixFmt != AV_PIX_FMT_NONE)
212 if (*PixFmt == AV_PIX_FMT_VAAPI)
214 return AV_PIX_FMT_VAAPI;
217 return AV_PIX_FMT_NONE;
222 while (*PixFmt != AV_PIX_FMT_NONE)
224 if (*PixFmt == AV_PIX_FMT_VAAPI)
226 return AV_PIX_FMT_VAAPI;
229 return AV_PIX_FMT_NONE;
242 if (
auto * player =
GetPlayerUI(Context); player !=
nullptr)
243 if (
auto * render =
dynamic_cast<MythRenderOpenGL*
>(player->GetRender()); render !=
nullptr)
254 auto * hwdeviceref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
255 if (!hwdeviceref || !hwdeviceref->data)
257 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware device context");
261 AVVAAPIDeviceContext * vaapidevicectx =
nullptr;
262 if (
auto * hwdevicecontext =
reinterpret_cast<AVHWDeviceContext*
>(hwdeviceref->data); hwdevicecontext !=
nullptr)
263 if (vaapidevicectx =
reinterpret_cast<AVVAAPIDeviceContext*
>(hwdevicecontext->hwctx); !vaapidevicectx)
267 vaapidevicectx->display = interop->
GetDisplay();
270 int res = av_hwdevice_ctx_init(hwdeviceref);
273 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI hardware context");
274 av_buffer_unref(&hwdeviceref);
280 Context->hw_frames_ctx = av_hwframe_ctx_alloc(hwdeviceref);
281 if (!Context->hw_frames_ctx)
283 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware frames context");
284 av_buffer_unref(&hwdeviceref);
290 auto * hw_frames_ctx =
reinterpret_cast<AVHWFramesContext*
>(Context->hw_frames_ctx->data);
291 auto * vaapi_frames_ctx =
reinterpret_cast<AVVAAPIFramesContext*
>(hw_frames_ctx->hwctx);
301 if (vendor.contains(
"i965", Qt::CaseInsensitive))
303 int format = VA_FOURCC_NV12;
304 if (vendor.contains(
"ironlake", Qt::CaseInsensitive))
308 if (format != VA_FOURCC_NV12)
311 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Forcing surface format for %1 and %2 with driver '%3'")
315 std::array<VASurfaceAttrib,3> prefs {{
316 { .type=VASurfaceAttribPixelFormat,
317 .flags=VA_SURFACE_ATTRIB_SETTABLE,
318 .value={ VAGenericValueTypeInteger, { format } } },
319 { .type=VASurfaceAttribUsageHint,
320 .flags=VA_SURFACE_ATTRIB_SETTABLE,
321 .value={ VAGenericValueTypeInteger, { VA_SURFACE_ATTRIB_USAGE_HINT_DISPLAY } } },
322 { .type=VASurfaceAttribMemoryType,
323 .flags=VA_SURFACE_ATTRIB_SETTABLE,
324 .value={ VAGenericValueTypeInteger, { VA_SURFACE_ATTRIB_MEM_TYPE_VA} } } }};
325 vaapi_frames_ctx->attributes = prefs.data();
326 vaapi_frames_ctx->nb_attributes = 3;
329 hw_frames_ctx->sw_format =
FramesFormat(Context->sw_pix_fmt);
332 hw_frames_ctx->format = AV_PIX_FMT_VAAPI;
333 hw_frames_ctx->width = Context->coded_width;
334 hw_frames_ctx->height = Context->coded_height;
336 hw_frames_ctx->user_opaque = interop;
341 res = av_hwframe_ctx_init(Context->hw_frames_ctx);
344 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI frames context");
345 av_buffer_unref(&hwdeviceref);
346 av_buffer_unref(&(Context->hw_frames_ctx));
350 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI FFmpeg buffer pool created with %1 %2x%3 surfaces (%4 references)")
351 .arg(vaapi_frames_ctx->nb_surfaces).arg(Context->coded_width).arg(Context->coded_height)
352 .arg(referenceframes));
353 av_buffer_unref(&hwdeviceref);
374 Context->hw_frames_ctx = av_hwframe_ctx_alloc(device);
375 if (!Context->hw_frames_ctx)
377 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware frames context");
378 av_buffer_unref(&device);
383 auto * hw_frames_ctx =
reinterpret_cast<AVHWFramesContext*
>(Context->hw_frames_ctx->data);
384 auto * vaapi_frames_ctx =
reinterpret_cast<AVVAAPIFramesContext*
>(hw_frames_ctx->hwctx);
385 hw_frames_ctx->sw_format =
FramesFormat(Context->sw_pix_fmt);
386 hw_frames_ctx->format = AV_PIX_FMT_VAAPI;
387 hw_frames_ctx->width = Context->coded_width;
388 hw_frames_ctx->height = Context->coded_height;
391 if (av_hwframe_ctx_init(Context->hw_frames_ctx) < 0)
393 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI frames context");
394 av_buffer_unref(&device);
395 av_buffer_unref(&(Context->hw_frames_ctx));
399 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI FFmpeg buffer pool created with %1 %2x%3 surfaces (%4 references)")
400 .arg(vaapi_frames_ctx->nb_surfaces).arg(Context->coded_width).arg(Context->coded_height)
401 .arg(referenceframes));
402 av_buffer_unref(&device);
418 static QString s_vendor;
419 static bool s_checked =
false;
420 if (s_checked && !ReCheck)
428 auto * hwdevice =
reinterpret_cast<AVHWDeviceContext*
>(context->data);
429 auto * hwctx =
reinterpret_cast<AVVAAPIDeviceContext*
>(hwdevice->hwctx);
430 s_vendor = QString(vaQueryVendorString(hwctx->display));
431 if (s_vendor.contains(
"vdpau", Qt::CaseInsensitive))
433 s_vendor = QString();
434 LOG(VB_GENERAL, LOG_INFO,
LOC +
"VAAPI is using a VDPAU backend - ignoring VAAPI");
436 else if (s_vendor.isEmpty())
438 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Unknown VAAPI vendor - ignoring VAAPI");
442 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Supported/available VAAPI decoders:");
444 for (
const auto &
profile : std::as_const(profiles))
448 LOG(VB_GENERAL, LOG_INFO,
LOC +
453 av_buffer_unref(&context);
457 LOG(VB_GENERAL, LOG_INFO,
LOC +
"VAAPI functionality checked failed");
465 static QRecursiveMutex lock;
466 static bool s_initialised =
false;
469 QMutexLocker locker(&lock);
472 s_initialised =
true;
474 auto VAToMythProfile = [](VAProfile Profile)
506 auto * device =
reinterpret_cast<AVHWDeviceContext*
>(hwdevicectx->data);
507 auto * hwctx =
reinterpret_cast<AVVAAPIDeviceContext*
>(device->hwctx);
509 int profilecount = vaMaxNumProfiles(hwctx->display);
510 auto * profilelist =
static_cast<VAProfile*
>(av_malloc_array(
static_cast<size_t>(profilecount),
sizeof(VAProfile)));
511 if (vaQueryConfigProfiles(hwctx->display, profilelist, &profilecount) == VA_STATUS_SUCCESS)
513 for (
auto i = 0; i < profilecount; ++i)
515 VAProfile
profile = profilelist[i];
516 if (
profile == VAProfileNone ||
profile == VAProfileH264StereoHigh ||
profile == VAProfileH264MultiviewHigh)
519 int entrysize = vaMaxNumEntrypoints(hwctx->display);
520 auto * entrylist =
static_cast<VAEntrypoint*
>(av_malloc_array(
static_cast<size_t>(entrysize),
sizeof(VAEntrypoint)));
521 if (vaQueryConfigEntrypoints(hwctx->display,
profile, entrylist, &count) == VA_STATUS_SUCCESS)
523 for (
int j = 0; j < count; ++j)
525 if (entrylist[j] != VAEntrypointVLD)
530 VAConfigID config = 0;
531 if (vaCreateConfig(hwctx->display,
profile, VAEntrypointVLD,
nullptr, 0, &config) != VA_STATUS_SUCCESS)
535 if (vaQuerySurfaceAttributes(hwctx->display, config,
nullptr, &attrcount) == VA_STATUS_SUCCESS)
537 auto * attrlist =
static_cast<VASurfaceAttrib*
>(av_malloc(attrcount *
sizeof(VASurfaceAttrib)));
538 if (vaQuerySurfaceAttributes(hwctx->display, config, attrlist, &attrcount) == VA_STATUS_SUCCESS)
540 for (
uint k = 0; k < attrcount; ++k)
542 if (attrlist[k].
type == VASurfaceAttribMaxWidth)
543 maxsize.setWidth(attrlist[k].value.value.i);
544 if (attrlist[k].
type == VASurfaceAttribMaxHeight)
545 maxsize.setHeight(attrlist[k].value.value.i);
546 if (attrlist[k].
type == VASurfaceAttribMinWidth)
547 minsize.setWidth(attrlist[k].value.value.i);
548 if (attrlist[k].
type == VASurfaceAttribMinHeight)
549 minsize.setHeight(attrlist[k].value.value.i);
552 av_freep(
reinterpret_cast<void*
>(&attrlist));
554 vaDestroyConfig(hwctx->display, config);
555 s_profiles.append(
VAAPIProfile(VAToMythProfile(
profile), QPair<QSize,QSize>(minsize, maxsize)));
558 av_freep(
reinterpret_cast<void*
>(&entrylist));
561 av_freep(
reinterpret_cast<void*
>(&profilelist));
562 av_buffer_unref(&hwdevicectx);
569 if (profiles.isEmpty())
571 Decoders.append(
"VAAPI:");
572 for (
const auto &
profile : std::as_const(profiles))
583 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
589 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
590 DirectRendering =
false;
599 if (AvFrame->format != AV_PIX_FMT_VAAPI)
622 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Input changed - deleting filter");
634 av_frame_remove_side_data(
Frame, AV_FRAME_DATA_A53_CC);
642 if (ret != AVERROR(EAGAIN))
647 ret = avcodec_receive_frame(Context,
Frame);
686 Frame->m_deinterlaceAllowed =
Frame->m_deinterlaceAllowed & ~DEINT_DRIVER;
697 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Disabling VAAPI VPP deinterlacer for %1")
712 Frame->m_alreadyDeinterlaced =
true;
721 bool doublerate =
true;
726 vaapideint = doublepref;
730 if (!vaapideint && !other && singlepref)
733 vaapideint = singlepref;
750 m_framesCtx = av_buffer_ref(Context->hw_frames_ctx);
752 Context->coded_width, Context->coded_height,
755 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to create deinterlacer %1 - disabling")
798 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Destroying VAAPI deinterlacer");
static int InitialiseDecoder(AVCodecContext *Context, CreateHWDecoder Callback, const QString &Debug)
Initialise a hardware decoder that is expected to use AVHWFramesContext.
static bool FrameTypeIsSupported(AVCodecContext *Context, VideoFrameType Format)
@ H264ConstrainedBaseline
static int GetBuffer(struct AVCodecContext *Context, AVFrame *Frame, int Flags)
A generic hardware buffer initialisation method when using AVHWFramesContext.
virtual bool RetrieveHWFrame(MythVideoFrame *Frame, AVFrame *AvFrame)
static AVBufferRef * CreateDevice(AVHWDeviceType Type, MythInteropGPU *Interop, const QString &Device=QString())
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)
static CodecProfile FFmpegToMythProfile(AVCodecID CodecID, int Profile)
QString GetSetting(const QString &key, const QString &defaultval="")
static QString TypeToString(InteropType Type)
static int InitialiseContext2(AVCodecContext *Context)
Create a VAAPI hardware context without OpenGL interop.
~MythVAAPIContext() override
static AVPixelFormat FramesFormat(AVPixelFormat Format)
void DestroyDeinterlacer()
static QString HaveVAAPI(bool ReCheck=false)
Check whether VAAPI is available and not emulated via VDPAU.
std::array< int64_t, 2 > m_filterPriorPTS
bool IsDeinterlacing(bool &DoubleRate, bool StreamChange=false) override
static int InitialiseContext(AVCodecContext *Context)
Create a VAAPI hardware context with appropriate OpenGL interop.
static VAProfile VAAPIProfileForCodec(const AVCodecContext *Codec)
MythDeintType m_deinterlacer
static const VAAPIProfiles & GetProfiles()
static enum AVPixelFormat GetFormat(AVCodecContext *Context, const AVPixelFormat *PixFmt)
AVFilterContext * m_filterSource
void PostProcessFrame(AVCodecContext *Context, MythVideoFrame *Frame) override
AVFilterGraph * m_filterGraph
static MythCodecID GetSupportedCodec(AVCodecContext **Context, const AVCodec **Codec, const QString &Decoder, uint StreamType)
Confirm whether VAAPI support is available given Decoder and Context.
MythVAAPIContext(DecoderBase *Parent, MythCodecID CodecID)
static enum AVPixelFormat GetFormat2(AVCodecContext *Context, const AVPixelFormat *PixFmt)
bool RetrieveFrame(AVCodecContext *Context, MythVideoFrame *Frame, AVFrame *AvFrame) override
bool DecoderWillResetOnFlush() override
int FilteredReceiveFrame(AVCodecContext *Context, AVFrame *Frame) override
Retrieve decoded frame and optionally deinterlace.
bool DecoderWillResetOnAspect() override
AVFilterContext * m_filterSink
AVBufferRef * m_framesCtx
static void GetDecoderList(QStringList &Decoders)
void InitVideoCodec(AVCodecContext *Context, bool SelectedStream, bool &DirectRendering) override
static bool SetupDeinterlacer(MythDeintType Deinterlacer, bool DoubleRate, AVBufferRef *FramesContext, int Width, int Height, AVFilterGraph *&Graph, AVFilterContext *&Source, AVFilterContext *&Sink)
static MythVAAPIInterop * CreateVAAPI(MythPlayerUI *Player, MythRenderOpenGL *Context)
VADisplay GetDisplay(void)
static QString DeinterlacerName(MythDeintType Deint, bool DoubleRate, VideoFrameType Format=FMT_NONE)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
static uint GetNumBuffers(int PixelFormat, int MaxReferenceFrames=16, bool Decoder=false)
uint mpeg_version(AVCodecID codec_id)
static bool CODEC_IS_MPEG(AVCodecID id)
static bool codec_is_vaapi(MythCodecID id)
static bool codec_is_vaapi_dec(MythCodecID id)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QVector< VAAPIProfile > VAAPIProfiles
QPair< MythCodecContext::CodecProfile, QPair< QSize, QSize > > VAAPIProfile
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
std::chrono::duration< CHRONO_TYPE, std::ratio< 1, 90000 > > pts