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 #if VA_CHECK_VERSION(0, 37, 0)
79 switch (Codec->profile)
81 case FF_PROFILE_HEVC_MAIN:
return VAProfileHEVCMain;
82 case FF_PROFILE_HEVC_MAIN_10:
return VAProfileHEVCMain10;
87 case AV_CODEC_ID_MJPEG:
return VAProfileJPEGBaseline;
88 case AV_CODEC_ID_WMV3:
90 switch (Codec->profile)
92 case FF_PROFILE_VC1_SIMPLE:
return VAProfileVC1Simple;
93 case FF_PROFILE_VC1_MAIN:
return VAProfileVC1Main;
94 case FF_PROFILE_VC1_ADVANCED:
95 case FF_PROFILE_VC1_COMPLEX:
return VAProfileVC1Advanced;
99 case AV_CODEC_ID_VP8:
return VAProfileVP8Version0_3;
100 case AV_CODEC_ID_VP9:
101 switch (Codec->profile)
103 #if VA_CHECK_VERSION(0, 38, 0)
104 case FF_PROFILE_VP9_0:
return VAProfileVP9Profile0;
106 #if VA_CHECK_VERSION(0, 39, 0)
107 case FF_PROFILE_VP9_2:
return VAProfileVP9Profile2;
115 return VAProfileNone;
122 case AV_PIX_FMT_YUV420P10:
return AV_PIX_FMT_P010;
123 case AV_PIX_FMT_YUV420P12:
124 case AV_PIX_FMT_YUV420P14:
125 case AV_PIX_FMT_YUV420P16:
return AV_PIX_FMT_P016;
126 default:
return AV_PIX_FMT_NV12;
137 bool decodeonly =
Decoder ==
"vaapi-dec";
141 if (!
Decoder.startsWith(
"vaapi") || vendor.isEmpty() || qEnvironmentVariableIsSet(
"NO_VAAPI"))
144 const auto * codec = avcodec_get_name((*Context)->codec_id);
145 const auto *
profile = avcodec_profile_name((*Context)->codec_id, (*Context)->profile);
146 const auto * pixfmt = av_get_pix_fmt_name((*Context)->pix_fmt);
150 if (desired == VAProfileNone)
152 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI does not support decoding '%1 %2 %3'")
161 if (vendor.contains(
"ironlake", Qt::CaseInsensitive))
163 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Disallowing VAAPI decode only for Ironlake");
179 return std::any_of(profiles.cbegin(), profiles.cend(),
180 [&Profile,Size](
auto vaprofile)
181 { return vaprofile.first == Profile &&
182 vaprofile.second.first.width() <= Size.width() &&
183 vaprofile.second.first.height() <= Size.height() &&
184 vaprofile.second.second.width() >= Size.width() &&
185 vaprofile.second.second.height() >= Size.height(); } );
188 ok = haveprofile(mythprofile, QSize((*Context)->width, (*Context)->height));
190 if (ok && (AV_PIX_FMT_YUVJ420P == (*Context)->pix_fmt || AV_PIX_FMT_YUVJ422P == (*Context)->pix_fmt ||
191 AV_PIX_FMT_YUVJ444P == (*Context)->pix_fmt))
196 auto desc = QString(
"'%1 %2 %3 %4x%5'").arg(codec,
profile, pixfmt)
197 .arg((*Context)->width).arg((*Context)->height);
201 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI supports decoding %1").arg(desc));
202 (*Context)->pix_fmt = AV_PIX_FMT_VAAPI;
206 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI does NOT support %1").arg(desc));
212 while (*PixFmt != AV_PIX_FMT_NONE)
214 if (*PixFmt == AV_PIX_FMT_VAAPI)
216 return AV_PIX_FMT_VAAPI;
219 return AV_PIX_FMT_NONE;
224 while (*PixFmt != AV_PIX_FMT_NONE)
226 if (*PixFmt == AV_PIX_FMT_VAAPI)
228 return AV_PIX_FMT_VAAPI;
231 return AV_PIX_FMT_NONE;
244 if (
auto * player =
GetPlayerUI(Context); player !=
nullptr)
245 if (
auto * render =
dynamic_cast<MythRenderOpenGL*
>(player->GetRender()); render !=
nullptr)
256 auto * hwdeviceref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
257 if (!hwdeviceref || !hwdeviceref->data)
259 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware device context");
263 AVVAAPIDeviceContext * vaapidevicectx =
nullptr;
264 if (
auto * hwdevicecontext =
reinterpret_cast<AVHWDeviceContext*
>(hwdeviceref->data); hwdevicecontext !=
nullptr)
265 if (vaapidevicectx =
reinterpret_cast<AVVAAPIDeviceContext*
>(hwdevicecontext->hwctx); !vaapidevicectx)
269 vaapidevicectx->display = interop->
GetDisplay();
272 int res = av_hwdevice_ctx_init(hwdeviceref);
275 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI hardware context");
276 av_buffer_unref(&hwdeviceref);
282 Context->hw_frames_ctx = av_hwframe_ctx_alloc(hwdeviceref);
283 if (!Context->hw_frames_ctx)
285 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware frames context");
286 av_buffer_unref(&hwdeviceref);
292 auto * hw_frames_ctx =
reinterpret_cast<AVHWFramesContext*
>(Context->hw_frames_ctx->data);
293 auto * vaapi_frames_ctx =
reinterpret_cast<AVVAAPIFramesContext*
>(hw_frames_ctx->hwctx);
304 if (vendor.contains(
"iHD", Qt::CaseInsensitive) && vendor.contains(
"Intel", Qt::CaseInsensitive))
306 vaapi_frames_ctx->attributes =
nullptr;
307 vaapi_frames_ctx->nb_attributes = 0;
312 int format = VA_FOURCC_NV12;
313 if (vendor.contains(
"ironlake", Qt::CaseInsensitive))
317 if (format != VA_FOURCC_NV12)
320 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Forcing surface format for %1 and %2 with driver '%3'")
324 std::array<VASurfaceAttrib,3> prefs {{
325 { VASurfaceAttribPixelFormat, VA_SURFACE_ATTRIB_SETTABLE, { VAGenericValueTypeInteger, { format } } },
326 { VASurfaceAttribUsageHint, VA_SURFACE_ATTRIB_SETTABLE, { VAGenericValueTypeInteger, { VA_SURFACE_ATTRIB_USAGE_HINT_DISPLAY } } },
327 { VASurfaceAttribMemoryType, VA_SURFACE_ATTRIB_SETTABLE, { VAGenericValueTypeInteger, { VA_SURFACE_ATTRIB_MEM_TYPE_VA} } } }};
328 vaapi_frames_ctx->attributes = prefs.data();
329 vaapi_frames_ctx->nb_attributes = 3;
332 hw_frames_ctx->sw_format =
FramesFormat(Context->sw_pix_fmt);
335 hw_frames_ctx->format = AV_PIX_FMT_VAAPI;
336 hw_frames_ctx->width = Context->coded_width;
337 hw_frames_ctx->height = Context->coded_height;
339 hw_frames_ctx->user_opaque = interop;
344 res = av_hwframe_ctx_init(Context->hw_frames_ctx);
347 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI frames context");
348 av_buffer_unref(&hwdeviceref);
349 av_buffer_unref(&(Context->hw_frames_ctx));
353 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI FFmpeg buffer pool created with %1 %2x%3 surfaces (%4 references)")
354 .arg(vaapi_frames_ctx->nb_surfaces).arg(Context->coded_width).arg(Context->coded_height)
355 .arg(referenceframes));
356 av_buffer_unref(&hwdeviceref);
377 Context->hw_frames_ctx = av_hwframe_ctx_alloc(device);
378 if (!Context->hw_frames_ctx)
380 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create VAAPI hardware frames context");
381 av_buffer_unref(&device);
386 auto * hw_frames_ctx =
reinterpret_cast<AVHWFramesContext*
>(Context->hw_frames_ctx->data);
387 auto * vaapi_frames_ctx =
reinterpret_cast<AVVAAPIFramesContext*
>(hw_frames_ctx->hwctx);
388 hw_frames_ctx->sw_format =
FramesFormat(Context->sw_pix_fmt);
389 hw_frames_ctx->format = AV_PIX_FMT_VAAPI;
390 hw_frames_ctx->width = Context->coded_width;
391 hw_frames_ctx->height = Context->coded_height;
394 if (av_hwframe_ctx_init(Context->hw_frames_ctx) < 0)
396 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI frames context");
397 av_buffer_unref(&device);
398 av_buffer_unref(&(Context->hw_frames_ctx));
402 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"VAAPI FFmpeg buffer pool created with %1 %2x%3 surfaces (%4 references)")
403 .arg(vaapi_frames_ctx->nb_surfaces).arg(Context->coded_width).arg(Context->coded_height)
404 .arg(referenceframes));
405 av_buffer_unref(&device);
421 static QString s_vendor;
422 static bool s_checked =
false;
423 if (s_checked && !ReCheck)
431 auto * hwdevice =
reinterpret_cast<AVHWDeviceContext*
>(context->data);
432 auto * hwctx =
reinterpret_cast<AVVAAPIDeviceContext*
>(hwdevice->hwctx);
433 s_vendor = QString(vaQueryVendorString(hwctx->display));
434 if (s_vendor.contains(
"vdpau", Qt::CaseInsensitive))
436 s_vendor = QString();
437 LOG(VB_GENERAL, LOG_INFO,
LOC +
"VAAPI is using a VDPAU backend - ignoring VAAPI");
439 else if (s_vendor.isEmpty())
441 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Unknown VAAPI vendor - ignoring VAAPI");
445 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Supported/available VAAPI decoders:");
447 for (
const auto &
profile : std::as_const(profiles))
451 LOG(VB_GENERAL, LOG_INFO,
LOC +
456 av_buffer_unref(&context);
460 LOG(VB_GENERAL, LOG_INFO,
LOC +
"VAAPI functionality checked failed");
468 static QRecursiveMutex lock;
469 static bool s_initialised =
false;
472 QMutexLocker locker(&lock);
475 s_initialised =
true;
477 auto VAToMythProfile = [](VAProfile Profile)
494 #if VA_CHECK_VERSION(0, 38, 0)
497 #if VA_CHECK_VERSION(0, 39, 0)
500 #if VA_CHECK_VERSION(0, 37, 0)
515 auto * device =
reinterpret_cast<AVHWDeviceContext*
>(hwdevicectx->data);
516 auto * hwctx =
reinterpret_cast<AVVAAPIDeviceContext*
>(device->hwctx);
518 int profilecount = vaMaxNumProfiles(hwctx->display);
519 auto * profilelist =
static_cast<VAProfile*
>(av_malloc_array(
static_cast<size_t>(profilecount),
sizeof(VAProfile)));
520 if (vaQueryConfigProfiles(hwctx->display, profilelist, &profilecount) == VA_STATUS_SUCCESS)
522 for (
auto i = 0; i < profilecount; ++i)
524 VAProfile
profile = profilelist[i];
525 if (
profile == VAProfileNone ||
profile == VAProfileH264StereoHigh ||
profile == VAProfileH264MultiviewHigh)
528 int entrysize = vaMaxNumEntrypoints(hwctx->display);
529 auto * entrylist =
static_cast<VAEntrypoint*
>(av_malloc_array(
static_cast<size_t>(entrysize),
sizeof(VAEntrypoint)));
530 if (vaQueryConfigEntrypoints(hwctx->display,
profile, entrylist, &count) == VA_STATUS_SUCCESS)
532 for (
int j = 0; j < count; ++j)
534 if (entrylist[j] != VAEntrypointVLD)
539 VAConfigID config = 0;
540 if (vaCreateConfig(hwctx->display,
profile, VAEntrypointVLD,
nullptr, 0, &config) != VA_STATUS_SUCCESS)
544 if (vaQuerySurfaceAttributes(hwctx->display, config,
nullptr, &attrcount) == VA_STATUS_SUCCESS)
546 auto * attrlist =
static_cast<VASurfaceAttrib*
>(av_malloc(attrcount *
sizeof(VASurfaceAttrib)));
547 if (vaQuerySurfaceAttributes(hwctx->display, config, attrlist, &attrcount) == VA_STATUS_SUCCESS)
549 for (
uint k = 0; k < attrcount; ++k)
551 if (attrlist[k].
type == VASurfaceAttribMaxWidth)
552 maxsize.setWidth(attrlist[k].value.value.i);
553 if (attrlist[k].
type == VASurfaceAttribMaxHeight)
554 maxsize.setHeight(attrlist[k].value.value.i);
555 if (attrlist[k].
type == VASurfaceAttribMinWidth)
556 minsize.setWidth(attrlist[k].value.value.i);
557 if (attrlist[k].
type == VASurfaceAttribMinHeight)
558 minsize.setHeight(attrlist[k].value.value.i);
561 av_freep(
reinterpret_cast<void*
>(&attrlist));
563 vaDestroyConfig(hwctx->display, config);
564 s_profiles.append(
VAAPIProfile(VAToMythProfile(
profile), QPair<QSize,QSize>(minsize, maxsize)));
567 av_freep(
reinterpret_cast<void*
>(&entrylist));
570 av_freep(
reinterpret_cast<void*
>(&profilelist));
571 av_buffer_unref(&hwdevicectx);
578 if (profiles.isEmpty())
580 Decoders.append(
"VAAPI:");
581 for (
const auto &
profile : std::as_const(profiles))
592 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
598 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
599 DirectRendering =
false;
608 if (AvFrame->format != AV_PIX_FMT_VAAPI)
631 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Input changed - deleting filter");
643 av_frame_remove_side_data(
Frame, AV_FRAME_DATA_A53_CC);
651 if (ret != AVERROR(EAGAIN))
656 ret = avcodec_receive_frame(Context,
Frame);
706 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Disabling VAAPI VPP deinterlacer for %1")
721 Frame->m_alreadyDeinterlaced =
true;
730 bool doublerate =
true;
735 vaapideint = doublepref;
739 if (!vaapideint && !other && singlepref)
742 vaapideint = singlepref;
759 m_framesCtx = av_buffer_ref(Context->hw_frames_ctx);
761 Context->coded_width, Context->coded_height,
764 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to create deinterlacer %1 - disabling")
807 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Destroying VAAPI deinterlacer");