2#include <QCoreApplication>
3#include <QWaitCondition>
18#include "libavutil/hwcontext_vaapi.h"
19#include "libavutil/pixdesc.h"
20#include "libavfilter/buffersink.h"
23#define LOC QString("VAAPIDec: ")
48 switch (Codec->codec_id)
50 case AV_CODEC_ID_MPEG2VIDEO:
51 switch (Codec->profile)
53 case FF_PROFILE_MPEG2_SIMPLE:
return VAProfileMPEG2Simple;
54 case FF_PROFILE_MPEG2_MAIN:
return VAProfileMPEG2Main;
58 case AV_CODEC_ID_H263:
return VAProfileH263Baseline;
59 case AV_CODEC_ID_MPEG4:
60 switch (Codec->profile)
62 case FF_PROFILE_MPEG4_SIMPLE:
return VAProfileMPEG4Simple;
63 case FF_PROFILE_MPEG4_ADVANCED_SIMPLE:
return VAProfileMPEG4AdvancedSimple;
64 case FF_PROFILE_MPEG4_MAIN:
return VAProfileMPEG4Main;
68 case AV_CODEC_ID_H264:
69 switch (Codec->profile)
71 case FF_PROFILE_H264_CONSTRAINED_BASELINE:
return VAProfileH264ConstrainedBaseline;
72 case FF_PROFILE_H264_MAIN:
return VAProfileH264Main;
73 case FF_PROFILE_H264_HIGH:
return VAProfileH264High;
77 case AV_CODEC_ID_HEVC:
78 switch (Codec->profile)
80 case FF_PROFILE_HEVC_MAIN:
return VAProfileHEVCMain;
81 case FF_PROFILE_HEVC_MAIN_10:
return VAProfileHEVCMain10;
85 case AV_CODEC_ID_MJPEG:
return VAProfileJPEGBaseline;
86 case AV_CODEC_ID_WMV3:
88 switch (Codec->profile)
90 case FF_PROFILE_VC1_SIMPLE:
return VAProfileVC1Simple;
91 case FF_PROFILE_VC1_MAIN:
return VAProfileVC1Main;
92 case FF_PROFILE_VC1_ADVANCED:
93 case FF_PROFILE_VC1_COMPLEX:
return VAProfileVC1Advanced;
97 case AV_CODEC_ID_VP8:
return VAProfileVP8Version0_3;
99 switch (Codec->profile)
101 case FF_PROFILE_VP9_0:
return VAProfileVP9Profile0;
102 case FF_PROFILE_VP9_2:
return VAProfileVP9Profile2;
109 return VAProfileNone;
116 case AV_PIX_FMT_YUV420P10:
return AV_PIX_FMT_P010;
117 case AV_PIX_FMT_YUV420P12:
118 case AV_PIX_FMT_YUV420P14:
119 case AV_PIX_FMT_YUV420P16:
return AV_PIX_FMT_P016;
120 default:
return AV_PIX_FMT_NV12;
131 bool decodeonly =
Decoder ==
"vaapi-dec";
135 if (!
Decoder.startsWith(
"vaapi") || vendor.isEmpty() || qEnvironmentVariableIsSet(
"NO_VAAPI"))
138 const auto * codec = avcodec_get_name((*Context)->codec_id);
139 const auto *
profile = avcodec_profile_name((*Context)->codec_id, (*Context)->profile);
140 const auto * pixfmt = av_get_pix_fmt_name((*Context)->pix_fmt);
144 if (desired == VAProfileNone)
146 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI does not support decoding '%1 %2 %3'")
155 if (vendor.contains(
"ironlake", Qt::CaseInsensitive))
157 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Disallowing VAAPI decode only for Ironlake");
173 return std::any_of(profiles.cbegin(), profiles.cend(),
174 [&Profile,Size](
auto vaprofile)
175 { return vaprofile.first == Profile &&
176 vaprofile.second.first.width() <= Size.width() &&
177 vaprofile.second.first.height() <= Size.height() &&
178 vaprofile.second.second.width() >= Size.width() &&
179 vaprofile.second.second.height() >= Size.height(); } );
182 ok = haveprofile(mythprofile, QSize((*Context)->width, (*Context)->height));
184 if (ok && (AV_PIX_FMT_YUVJ420P == (*Context)->pix_fmt || AV_PIX_FMT_YUVJ422P == (*Context)->pix_fmt ||
185 AV_PIX_FMT_YUVJ444P == (*Context)->pix_fmt))
190 auto desc = QString(
"'%1 %2 %3 %4x%5'").arg(codec,
profile, pixfmt)
191 .arg((*Context)->width).arg((*Context)->height);
195 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI supports decoding %1").arg(desc));
196 (*Context)->pix_fmt = AV_PIX_FMT_VAAPI;
200 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI does NOT support %1").arg(desc));
206 while (*PixFmt != AV_PIX_FMT_NONE)
208 if (*PixFmt == AV_PIX_FMT_VAAPI)
210 return AV_PIX_FMT_VAAPI;
213 return AV_PIX_FMT_NONE;
218 while (*PixFmt != AV_PIX_FMT_NONE)
220 if (*PixFmt == AV_PIX_FMT_VAAPI)
222 return AV_PIX_FMT_VAAPI;
225 return AV_PIX_FMT_NONE;
238 if (
auto * player =
GetPlayerUI(Context); player !=
nullptr)
239 if (
auto * render =
dynamic_cast<MythRenderOpenGL*
>(player->GetRender()); render !=
nullptr)
250 auto * hwdeviceref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
251 if (!hwdeviceref || !hwdeviceref->data)
253 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware device context");
257 AVVAAPIDeviceContext * vaapidevicectx =
nullptr;
258 if (
auto * hwdevicecontext =
reinterpret_cast<AVHWDeviceContext*
>(hwdeviceref->data); hwdevicecontext !=
nullptr)
259 if (vaapidevicectx =
reinterpret_cast<AVVAAPIDeviceContext*
>(hwdevicecontext->hwctx); !vaapidevicectx)
263 vaapidevicectx->display = interop->
GetDisplay();
266 int res = av_hwdevice_ctx_init(hwdeviceref);
269 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI hardware context");
270 av_buffer_unref(&hwdeviceref);
276 Context->hw_frames_ctx = av_hwframe_ctx_alloc(hwdeviceref);
277 if (!Context->hw_frames_ctx)
279 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware frames context");
280 av_buffer_unref(&hwdeviceref);
286 auto * hw_frames_ctx =
reinterpret_cast<AVHWFramesContext*
>(Context->hw_frames_ctx->data);
287 auto * vaapi_frames_ctx =
reinterpret_cast<AVVAAPIFramesContext*
>(hw_frames_ctx->hwctx);
298 if (vendor.contains(
"iHD", Qt::CaseInsensitive) && vendor.contains(
"Intel", Qt::CaseInsensitive))
300 vaapi_frames_ctx->attributes =
nullptr;
301 vaapi_frames_ctx->nb_attributes = 0;
306 int format = VA_FOURCC_NV12;
307 if (vendor.contains(
"ironlake", Qt::CaseInsensitive))
311 if (format != VA_FOURCC_NV12)
314 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Forcing surface format for %1 and %2 with driver '%3'")
318 std::array<VASurfaceAttrib,3> prefs {{
319 { VASurfaceAttribPixelFormat, VA_SURFACE_ATTRIB_SETTABLE, { VAGenericValueTypeInteger, { format } } },
320 { VASurfaceAttribUsageHint, VA_SURFACE_ATTRIB_SETTABLE, { VAGenericValueTypeInteger, { VA_SURFACE_ATTRIB_USAGE_HINT_DISPLAY } } },
321 { VASurfaceAttribMemoryType, VA_SURFACE_ATTRIB_SETTABLE, { VAGenericValueTypeInteger, { VA_SURFACE_ATTRIB_MEM_TYPE_VA} } } }};
322 vaapi_frames_ctx->attributes = prefs.data();
323 vaapi_frames_ctx->nb_attributes = 3;
326 hw_frames_ctx->sw_format =
FramesFormat(Context->sw_pix_fmt);
329 hw_frames_ctx->format = AV_PIX_FMT_VAAPI;
330 hw_frames_ctx->width = Context->coded_width;
331 hw_frames_ctx->height = Context->coded_height;
333 hw_frames_ctx->user_opaque = interop;
338 res = av_hwframe_ctx_init(Context->hw_frames_ctx);
341 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI frames context");
342 av_buffer_unref(&hwdeviceref);
343 av_buffer_unref(&(Context->hw_frames_ctx));
347 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI FFmpeg buffer pool created with %1 %2x%3 surfaces (%4 references)")
348 .arg(vaapi_frames_ctx->nb_surfaces).arg(Context->coded_width).arg(Context->coded_height)
349 .arg(referenceframes));
350 av_buffer_unref(&hwdeviceref);
371 Context->hw_frames_ctx = av_hwframe_ctx_alloc(device);
372 if (!Context->hw_frames_ctx)
374 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware frames context");
375 av_buffer_unref(&device);
380 auto * hw_frames_ctx =
reinterpret_cast<AVHWFramesContext*
>(Context->hw_frames_ctx->data);
381 auto * vaapi_frames_ctx =
reinterpret_cast<AVVAAPIFramesContext*
>(hw_frames_ctx->hwctx);
382 hw_frames_ctx->sw_format =
FramesFormat(Context->sw_pix_fmt);
383 hw_frames_ctx->format = AV_PIX_FMT_VAAPI;
384 hw_frames_ctx->width = Context->coded_width;
385 hw_frames_ctx->height = Context->coded_height;
388 if (av_hwframe_ctx_init(Context->hw_frames_ctx) < 0)
390 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI frames context");
391 av_buffer_unref(&device);
392 av_buffer_unref(&(Context->hw_frames_ctx));
396 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI FFmpeg buffer pool created with %1 %2x%3 surfaces (%4 references)")
397 .arg(vaapi_frames_ctx->nb_surfaces).arg(Context->coded_width).arg(Context->coded_height)
398 .arg(referenceframes));
399 av_buffer_unref(&device);
415 static QString s_vendor;
416 static bool s_checked =
false;
417 if (s_checked && !ReCheck)
425 auto * hwdevice =
reinterpret_cast<AVHWDeviceContext*
>(context->data);
426 auto * hwctx =
reinterpret_cast<AVVAAPIDeviceContext*
>(hwdevice->hwctx);
427 s_vendor = QString(vaQueryVendorString(hwctx->display));
428 if (s_vendor.contains(
"vdpau", Qt::CaseInsensitive))
430 s_vendor = QString();
431 LOG(VB_GENERAL, LOG_INFO,
LOC +
"VAAPI is using a VDPAU backend - ignoring VAAPI");
433 else if (s_vendor.isEmpty())
435 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Unknown VAAPI vendor - ignoring VAAPI");
439 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Supported/available VAAPI decoders:");
441 for (
const auto &
profile : std::as_const(profiles))
445 LOG(VB_GENERAL, LOG_INFO,
LOC +
450 av_buffer_unref(&context);
454 LOG(VB_GENERAL, LOG_INFO,
LOC +
"VAAPI functionality checked failed");
462 static QRecursiveMutex lock;
463 static bool s_initialised =
false;
466 QMutexLocker locker(&lock);
469 s_initialised =
true;
471 auto VAToMythProfile = [](VAProfile Profile)
503 auto * device =
reinterpret_cast<AVHWDeviceContext*
>(hwdevicectx->data);
504 auto * hwctx =
reinterpret_cast<AVVAAPIDeviceContext*
>(device->hwctx);
506 int profilecount = vaMaxNumProfiles(hwctx->display);
507 auto * profilelist =
static_cast<VAProfile*
>(av_malloc_array(
static_cast<size_t>(profilecount),
sizeof(VAProfile)));
508 if (vaQueryConfigProfiles(hwctx->display, profilelist, &profilecount) == VA_STATUS_SUCCESS)
510 for (
auto i = 0; i < profilecount; ++i)
512 VAProfile
profile = profilelist[i];
513 if (
profile == VAProfileNone ||
profile == VAProfileH264StereoHigh ||
profile == VAProfileH264MultiviewHigh)
516 int entrysize = vaMaxNumEntrypoints(hwctx->display);
517 auto * entrylist =
static_cast<VAEntrypoint*
>(av_malloc_array(
static_cast<size_t>(entrysize),
sizeof(VAEntrypoint)));
518 if (vaQueryConfigEntrypoints(hwctx->display,
profile, entrylist, &count) == VA_STATUS_SUCCESS)
520 for (
int j = 0; j < count; ++j)
522 if (entrylist[j] != VAEntrypointVLD)
527 VAConfigID config = 0;
528 if (vaCreateConfig(hwctx->display,
profile, VAEntrypointVLD,
nullptr, 0, &config) != VA_STATUS_SUCCESS)
532 if (vaQuerySurfaceAttributes(hwctx->display, config,
nullptr, &attrcount) == VA_STATUS_SUCCESS)
534 auto * attrlist =
static_cast<VASurfaceAttrib*
>(av_malloc(attrcount *
sizeof(VASurfaceAttrib)));
535 if (vaQuerySurfaceAttributes(hwctx->display, config, attrlist, &attrcount) == VA_STATUS_SUCCESS)
537 for (
uint k = 0; k < attrcount; ++k)
539 if (attrlist[k].
type == VASurfaceAttribMaxWidth)
540 maxsize.setWidth(attrlist[k].value.value.i);
541 if (attrlist[k].
type == VASurfaceAttribMaxHeight)
542 maxsize.setHeight(attrlist[k].value.value.i);
543 if (attrlist[k].
type == VASurfaceAttribMinWidth)
544 minsize.setWidth(attrlist[k].value.value.i);
545 if (attrlist[k].
type == VASurfaceAttribMinHeight)
546 minsize.setHeight(attrlist[k].value.value.i);
549 av_freep(
reinterpret_cast<void*
>(&attrlist));
551 vaDestroyConfig(hwctx->display, config);
552 s_profiles.append(
VAAPIProfile(VAToMythProfile(
profile), QPair<QSize,QSize>(minsize, maxsize)));
555 av_freep(
reinterpret_cast<void*
>(&entrylist));
558 av_freep(
reinterpret_cast<void*
>(&profilelist));
559 av_buffer_unref(&hwdevicectx);
566 if (profiles.isEmpty())
568 Decoders.append(
"VAAPI:");
569 for (
const auto &
profile : std::as_const(profiles))
580 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
586 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
587 DirectRendering =
false;
596 if (AvFrame->format != AV_PIX_FMT_VAAPI)
619 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Input changed - deleting filter");
631 av_frame_remove_side_data(
Frame, AV_FRAME_DATA_A53_CC);
639 if (ret != AVERROR(EAGAIN))
644 ret = avcodec_receive_frame(Context,
Frame);
683 Frame->m_deinterlaceAllowed =
Frame->m_deinterlaceAllowed & ~DEINT_DRIVER;
694 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Disabling VAAPI VPP deinterlacer for %1")
709 Frame->m_alreadyDeinterlaced =
true;
718 bool doublerate =
true;
723 vaapideint = doublepref;
727 if (!vaapideint && !other && singlepref)
730 vaapideint = singlepref;
747 m_framesCtx = av_buffer_ref(Context->hw_frames_ctx);
749 Context->coded_width, Context->coded_height,
752 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to create deinterlacer %1 - disabling")
795 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