2#include <QCoreApplication>
3#include <QWaitCondition>
18#include "libavcodec/defs.h"
19#include "libavutil/hwcontext_vaapi.h"
20#include "libavutil/pixdesc.h"
21#include "libavfilter/buffersink.h"
24#define LOC QString("VAAPIDec: ")
49 switch (Codec->codec_id)
51 case AV_CODEC_ID_MPEG2VIDEO:
52 switch (Codec->profile)
54 case AV_PROFILE_MPEG2_SIMPLE:
return VAProfileMPEG2Simple;
55 case AV_PROFILE_MPEG2_MAIN:
return VAProfileMPEG2Main;
59 case AV_CODEC_ID_H263:
return VAProfileH263Baseline;
60 case AV_CODEC_ID_MPEG4:
61 switch (Codec->profile)
63 case AV_PROFILE_MPEG4_SIMPLE:
return VAProfileMPEG4Simple;
64 case AV_PROFILE_MPEG4_ADVANCED_SIMPLE:
return VAProfileMPEG4AdvancedSimple;
65 case AV_PROFILE_MPEG4_MAIN:
return VAProfileMPEG4Main;
69 case AV_CODEC_ID_H264:
70 switch (Codec->profile)
72 case AV_PROFILE_H264_CONSTRAINED_BASELINE:
return VAProfileH264ConstrainedBaseline;
73 case AV_PROFILE_H264_MAIN:
return VAProfileH264Main;
74 case AV_PROFILE_H264_HIGH:
return VAProfileH264High;
78 case AV_CODEC_ID_HEVC:
79 switch (Codec->profile)
81 case AV_PROFILE_HEVC_MAIN:
return VAProfileHEVCMain;
82 case AV_PROFILE_HEVC_MAIN_10:
return VAProfileHEVCMain10;
86 case AV_CODEC_ID_MJPEG:
return VAProfileJPEGBaseline;
87 case AV_CODEC_ID_WMV3:
89 switch (Codec->profile)
91 case AV_PROFILE_VC1_SIMPLE:
return VAProfileVC1Simple;
92 case AV_PROFILE_VC1_MAIN:
return VAProfileVC1Main;
93 case AV_PROFILE_VC1_ADVANCED:
94 case AV_PROFILE_VC1_COMPLEX:
return VAProfileVC1Advanced;
98 case AV_CODEC_ID_VP8:
return VAProfileVP8Version0_3;
100 switch (Codec->profile)
102 case AV_PROFILE_VP9_0:
return VAProfileVP9Profile0;
103 case AV_PROFILE_VP9_2:
return VAProfileVP9Profile2;
110 return VAProfileNone;
117 case AV_PIX_FMT_YUV420P10:
return AV_PIX_FMT_P010;
118 case AV_PIX_FMT_YUV420P12:
119 case AV_PIX_FMT_YUV420P14:
120 case AV_PIX_FMT_YUV420P16:
return AV_PIX_FMT_P016;
121 default:
return AV_PIX_FMT_NV12;
132 bool decodeonly =
Decoder ==
"vaapi-dec";
136 if (!
Decoder.startsWith(
"vaapi") || vendor.isEmpty() || qEnvironmentVariableIsSet(
"NO_VAAPI"))
139 const auto * codec = avcodec_get_name((*Context)->codec_id);
140 const auto *
profile = avcodec_profile_name((*Context)->codec_id, (*Context)->profile);
141 const auto * pixfmt = av_get_pix_fmt_name((*Context)->pix_fmt);
145 if (desired == VAProfileNone)
147 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI does not support decoding '%1 %2 %3'")
156 if (vendor.contains(
"ironlake", Qt::CaseInsensitive))
158 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Disallowing VAAPI decode only for Ironlake");
174 return std::any_of(profiles.cbegin(), profiles.cend(),
175 [&Profile,Size](
auto vaprofile)
176 { return vaprofile.first == Profile &&
177 vaprofile.second.first.width() <= Size.width() &&
178 vaprofile.second.first.height() <= Size.height() &&
179 vaprofile.second.second.width() >= Size.width() &&
180 vaprofile.second.second.height() >= Size.height(); } );
183 ok = haveprofile(mythprofile, QSize((*Context)->width, (*Context)->height));
185 if (ok && (AV_PIX_FMT_YUVJ420P == (*Context)->pix_fmt || AV_PIX_FMT_YUVJ422P == (*Context)->pix_fmt ||
186 AV_PIX_FMT_YUVJ444P == (*Context)->pix_fmt))
191 auto desc = QString(
"'%1 %2 %3 %4x%5'").arg(codec,
profile, pixfmt)
192 .arg((*Context)->width).arg((*Context)->height);
196 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI supports decoding %1").arg(desc));
197 (*Context)->pix_fmt = AV_PIX_FMT_VAAPI;
201 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI does NOT support %1").arg(desc));
207 while (*PixFmt != AV_PIX_FMT_NONE)
209 if (*PixFmt == AV_PIX_FMT_VAAPI)
211 return AV_PIX_FMT_VAAPI;
214 return AV_PIX_FMT_NONE;
219 while (*PixFmt != AV_PIX_FMT_NONE)
221 if (*PixFmt == AV_PIX_FMT_VAAPI)
223 return AV_PIX_FMT_VAAPI;
226 return AV_PIX_FMT_NONE;
239 if (
auto * player =
GetPlayerUI(Context); player !=
nullptr)
240 if (
auto * render =
dynamic_cast<MythRenderOpenGL*
>(player->GetRender()); render !=
nullptr)
251 auto * hwdeviceref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
252 if (!hwdeviceref || !hwdeviceref->data)
254 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware device context");
258 AVVAAPIDeviceContext * vaapidevicectx =
nullptr;
259 if (
auto * hwdevicecontext =
reinterpret_cast<AVHWDeviceContext*
>(hwdeviceref->data); hwdevicecontext !=
nullptr)
260 if (vaapidevicectx =
reinterpret_cast<AVVAAPIDeviceContext*
>(hwdevicecontext->hwctx); !vaapidevicectx)
264 vaapidevicectx->display = interop->
GetDisplay();
267 int res = av_hwdevice_ctx_init(hwdeviceref);
270 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI hardware context");
271 av_buffer_unref(&hwdeviceref);
277 Context->hw_frames_ctx = av_hwframe_ctx_alloc(hwdeviceref);
278 if (!Context->hw_frames_ctx)
280 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware frames context");
281 av_buffer_unref(&hwdeviceref);
287 auto * hw_frames_ctx =
reinterpret_cast<AVHWFramesContext*
>(Context->hw_frames_ctx->data);
288 auto * vaapi_frames_ctx =
reinterpret_cast<AVVAAPIFramesContext*
>(hw_frames_ctx->hwctx);
298 if (vendor.contains(
"i965", Qt::CaseInsensitive))
300 int format = VA_FOURCC_NV12;
301 if (vendor.contains(
"ironlake", Qt::CaseInsensitive))
305 if (format != VA_FOURCC_NV12)
308 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Forcing surface format for %1 and %2 with driver '%3'")
312 std::array<VASurfaceAttrib,3> prefs {{
313 { VASurfaceAttribPixelFormat, VA_SURFACE_ATTRIB_SETTABLE, { VAGenericValueTypeInteger, { format } } },
314 { VASurfaceAttribUsageHint, VA_SURFACE_ATTRIB_SETTABLE, { VAGenericValueTypeInteger, { VA_SURFACE_ATTRIB_USAGE_HINT_DISPLAY } } },
315 { VASurfaceAttribMemoryType, VA_SURFACE_ATTRIB_SETTABLE, { VAGenericValueTypeInteger, { VA_SURFACE_ATTRIB_MEM_TYPE_VA} } } }};
316 vaapi_frames_ctx->attributes = prefs.data();
317 vaapi_frames_ctx->nb_attributes = 3;
320 hw_frames_ctx->sw_format =
FramesFormat(Context->sw_pix_fmt);
323 hw_frames_ctx->format = AV_PIX_FMT_VAAPI;
324 hw_frames_ctx->width = Context->coded_width;
325 hw_frames_ctx->height = Context->coded_height;
327 hw_frames_ctx->user_opaque = interop;
332 res = av_hwframe_ctx_init(Context->hw_frames_ctx);
335 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI frames context");
336 av_buffer_unref(&hwdeviceref);
337 av_buffer_unref(&(Context->hw_frames_ctx));
341 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI FFmpeg buffer pool created with %1 %2x%3 surfaces (%4 references)")
342 .arg(vaapi_frames_ctx->nb_surfaces).arg(Context->coded_width).arg(Context->coded_height)
343 .arg(referenceframes));
344 av_buffer_unref(&hwdeviceref);
365 Context->hw_frames_ctx = av_hwframe_ctx_alloc(device);
366 if (!Context->hw_frames_ctx)
368 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware frames context");
369 av_buffer_unref(&device);
374 auto * hw_frames_ctx =
reinterpret_cast<AVHWFramesContext*
>(Context->hw_frames_ctx->data);
375 auto * vaapi_frames_ctx =
reinterpret_cast<AVVAAPIFramesContext*
>(hw_frames_ctx->hwctx);
376 hw_frames_ctx->sw_format =
FramesFormat(Context->sw_pix_fmt);
377 hw_frames_ctx->format = AV_PIX_FMT_VAAPI;
378 hw_frames_ctx->width = Context->coded_width;
379 hw_frames_ctx->height = Context->coded_height;
382 if (av_hwframe_ctx_init(Context->hw_frames_ctx) < 0)
384 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI frames context");
385 av_buffer_unref(&device);
386 av_buffer_unref(&(Context->hw_frames_ctx));
390 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI FFmpeg buffer pool created with %1 %2x%3 surfaces (%4 references)")
391 .arg(vaapi_frames_ctx->nb_surfaces).arg(Context->coded_width).arg(Context->coded_height)
392 .arg(referenceframes));
393 av_buffer_unref(&device);
409 static QString s_vendor;
410 static bool s_checked =
false;
411 if (s_checked && !ReCheck)
419 auto * hwdevice =
reinterpret_cast<AVHWDeviceContext*
>(context->data);
420 auto * hwctx =
reinterpret_cast<AVVAAPIDeviceContext*
>(hwdevice->hwctx);
421 s_vendor = QString(vaQueryVendorString(hwctx->display));
422 if (s_vendor.contains(
"vdpau", Qt::CaseInsensitive))
424 s_vendor = QString();
425 LOG(VB_GENERAL, LOG_INFO,
LOC +
"VAAPI is using a VDPAU backend - ignoring VAAPI");
427 else if (s_vendor.isEmpty())
429 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Unknown VAAPI vendor - ignoring VAAPI");
433 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Supported/available VAAPI decoders:");
435 for (
const auto &
profile : std::as_const(profiles))
439 LOG(VB_GENERAL, LOG_INFO,
LOC +
444 av_buffer_unref(&context);
448 LOG(VB_GENERAL, LOG_INFO,
LOC +
"VAAPI functionality checked failed");
456 static QRecursiveMutex lock;
457 static bool s_initialised =
false;
460 QMutexLocker locker(&lock);
463 s_initialised =
true;
465 auto VAToMythProfile = [](VAProfile Profile)
497 auto * device =
reinterpret_cast<AVHWDeviceContext*
>(hwdevicectx->data);
498 auto * hwctx =
reinterpret_cast<AVVAAPIDeviceContext*
>(device->hwctx);
500 int profilecount = vaMaxNumProfiles(hwctx->display);
501 auto * profilelist =
static_cast<VAProfile*
>(av_malloc_array(
static_cast<size_t>(profilecount),
sizeof(VAProfile)));
502 if (vaQueryConfigProfiles(hwctx->display, profilelist, &profilecount) == VA_STATUS_SUCCESS)
504 for (
auto i = 0; i < profilecount; ++i)
506 VAProfile
profile = profilelist[i];
507 if (
profile == VAProfileNone ||
profile == VAProfileH264StereoHigh ||
profile == VAProfileH264MultiviewHigh)
510 int entrysize = vaMaxNumEntrypoints(hwctx->display);
511 auto * entrylist =
static_cast<VAEntrypoint*
>(av_malloc_array(
static_cast<size_t>(entrysize),
sizeof(VAEntrypoint)));
512 if (vaQueryConfigEntrypoints(hwctx->display,
profile, entrylist, &count) == VA_STATUS_SUCCESS)
514 for (
int j = 0; j < count; ++j)
516 if (entrylist[j] != VAEntrypointVLD)
521 VAConfigID config = 0;
522 if (vaCreateConfig(hwctx->display,
profile, VAEntrypointVLD,
nullptr, 0, &config) != VA_STATUS_SUCCESS)
526 if (vaQuerySurfaceAttributes(hwctx->display, config,
nullptr, &attrcount) == VA_STATUS_SUCCESS)
528 auto * attrlist =
static_cast<VASurfaceAttrib*
>(av_malloc(attrcount *
sizeof(VASurfaceAttrib)));
529 if (vaQuerySurfaceAttributes(hwctx->display, config, attrlist, &attrcount) == VA_STATUS_SUCCESS)
531 for (
uint k = 0; k < attrcount; ++k)
533 if (attrlist[k].
type == VASurfaceAttribMaxWidth)
534 maxsize.setWidth(attrlist[k].value.value.i);
535 if (attrlist[k].
type == VASurfaceAttribMaxHeight)
536 maxsize.setHeight(attrlist[k].value.value.i);
537 if (attrlist[k].
type == VASurfaceAttribMinWidth)
538 minsize.setWidth(attrlist[k].value.value.i);
539 if (attrlist[k].
type == VASurfaceAttribMinHeight)
540 minsize.setHeight(attrlist[k].value.value.i);
543 av_freep(
reinterpret_cast<void*
>(&attrlist));
545 vaDestroyConfig(hwctx->display, config);
546 s_profiles.append(
VAAPIProfile(VAToMythProfile(
profile), QPair<QSize,QSize>(minsize, maxsize)));
549 av_freep(
reinterpret_cast<void*
>(&entrylist));
552 av_freep(
reinterpret_cast<void*
>(&profilelist));
553 av_buffer_unref(&hwdevicectx);
560 if (profiles.isEmpty())
562 Decoders.append(
"VAAPI:");
563 for (
const auto &
profile : std::as_const(profiles))
574 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
580 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
581 DirectRendering =
false;
590 if (AvFrame->format != AV_PIX_FMT_VAAPI)
613 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Input changed - deleting filter");
625 av_frame_remove_side_data(
Frame, AV_FRAME_DATA_A53_CC);
633 if (ret != AVERROR(EAGAIN))
638 ret = avcodec_receive_frame(Context,
Frame);
677 Frame->m_deinterlaceAllowed =
Frame->m_deinterlaceAllowed & ~DEINT_DRIVER;
688 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Disabling VAAPI VPP deinterlacer for %1")
703 Frame->m_alreadyDeinterlaced =
true;
712 bool doublerate =
true;
717 vaapideint = doublepref;
721 if (!vaapideint && !other && singlepref)
724 vaapideint = singlepref;
741 m_framesCtx = av_buffer_ref(Context->hw_frames_ctx);
743 Context->coded_width, Context->coded_height,
746 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to create deinterlacer %1 - disabling")
789 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