MythTV master
mythvaapicontext.cpp
Go to the documentation of this file.
1// Qt
2#include <QCoreApplication>
3#include <QWaitCondition>
4
5// Mythtv
10
12#include "mythplayerui.h"
13#include "mythvaapicontext.h"
15#include "videobuffers.h"
16
17extern "C" {
18#include "libavutil/hwcontext_vaapi.h"
19#include "libavutil/pixdesc.h"
20#include "libavfilter/buffersink.h"
21}
22
23#define LOC QString("VAAPIDec: ")
24
34 : MythCodecContext(Parent, CodecID)
35{
36}
37
39{
41}
42
43VAProfile MythVAAPIContext::VAAPIProfileForCodec(const AVCodecContext* Codec)
44{
45 if (!Codec)
46 return VAProfileNone;
47
48 switch (Codec->codec_id)
49 {
50 case AV_CODEC_ID_MPEG2VIDEO:
51 switch (Codec->profile)
52 {
53 case FF_PROFILE_MPEG2_SIMPLE: return VAProfileMPEG2Simple;
54 case FF_PROFILE_MPEG2_MAIN: return VAProfileMPEG2Main;
55 default: break;
56 }
57 break;
58 case AV_CODEC_ID_H263: return VAProfileH263Baseline;
59 case AV_CODEC_ID_MPEG4:
60 switch (Codec->profile)
61 {
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;
65 default: break;
66 }
67 break;
68 case AV_CODEC_ID_H264:
69 switch (Codec->profile)
70 {
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;
74 default: break;
75 }
76 break;
77 case AV_CODEC_ID_HEVC:
78#if VA_CHECK_VERSION(0, 37, 0)
79 switch (Codec->profile)
80 {
81 case FF_PROFILE_HEVC_MAIN: return VAProfileHEVCMain;
82 case FF_PROFILE_HEVC_MAIN_10: return VAProfileHEVCMain10;
83 default: break;
84 }
85#endif
86 break;
87 case AV_CODEC_ID_MJPEG: return VAProfileJPEGBaseline;
88 case AV_CODEC_ID_WMV3:
89 case AV_CODEC_ID_VC1:
90 switch (Codec->profile)
91 {
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;
96 default: break;
97 }
98 break;
99 case AV_CODEC_ID_VP8: return VAProfileVP8Version0_3;
100 case AV_CODEC_ID_VP9:
101 switch (Codec->profile)
102 {
103#if VA_CHECK_VERSION(0, 38, 0)
104 case FF_PROFILE_VP9_0: return VAProfileVP9Profile0;
105#endif
106#if VA_CHECK_VERSION(0, 39, 0)
107 case FF_PROFILE_VP9_2: return VAProfileVP9Profile2;
108#endif
109 default: break;
110 }
111 break;
112 default: break;
113 }
114
115 return VAProfileNone;
116}
117
118inline AVPixelFormat MythVAAPIContext::FramesFormat(AVPixelFormat Format)
119{
120 switch (Format)
121 {
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;
127 }
128}
129
133 const AVCodec** /*Codec*/,
134 const QString& Decoder,
135 uint StreamType)
136{
137 bool decodeonly = Decoder == "vaapi-dec";
138 auto success = static_cast<MythCodecID>((decodeonly ? kCodec_MPEG1_VAAPI_DEC : kCodec_MPEG1_VAAPI) + (StreamType - 1));
139 auto failure = static_cast<MythCodecID>(kCodec_MPEG1 + (StreamType - 1));
140 auto vendor = HaveVAAPI();
141 if (!Decoder.startsWith("vaapi") || vendor.isEmpty() || qEnvironmentVariableIsSet("NO_VAAPI"))
142 return failure;
143
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);
147
148 // Simple check for known profile
149 auto desired = VAAPIProfileForCodec(*Context);
150 if (desired == VAProfileNone)
151 {
152 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VAAPI does not support decoding '%1 %2 %3'")
153 .arg(codec, profile, pixfmt));
154 return failure;
155 }
156
157 // Check for ironlake decode only - which won't work due to FFmpeg frame format
158 // constraints. May apply to other platforms.
159 if (decodeonly)
160 {
161 if (vendor.contains("ironlake", Qt::CaseInsensitive))
162 {
163 LOG(VB_GENERAL, LOG_WARNING, LOC + "Disallowing VAAPI decode only for Ironlake");
164 return failure;
165 }
166 }
167 else
168 {
169 if (!FrameTypeIsSupported(*Context, FMT_VAAPI))
170 return failure;
171 }
172
173 // Check profile support
174 bool ok = false;
175 const auto & profiles = MythVAAPIContext::GetProfiles();
176 auto mythprofile = MythCodecContext::FFmpegToMythProfile((*Context)->codec_id, (*Context)->profile);
177 auto haveprofile = [&](MythCodecContext::CodecProfile Profile, QSize Size)
178 {
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(); } );
186 };
187
188 ok = haveprofile(mythprofile, QSize((*Context)->width, (*Context)->height));
189 // use JPEG support as a proxy for MJPEG (full range YUV)
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))
192 {
193 ok = haveprofile(MythCodecContext::MJPEG, QSize());
194 }
195
196 auto desc = QString("'%1 %2 %3 %4x%5'").arg(codec, profile, pixfmt)
197 .arg((*Context)->width).arg((*Context)->height);
198
199 if (ok)
200 {
201 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VAAPI supports decoding %1").arg(desc));
202 (*Context)->pix_fmt = AV_PIX_FMT_VAAPI;
203 return success;
204 }
205
206 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VAAPI does NOT support %1").arg(desc));
207 return failure;
208}
209
210AVPixelFormat MythVAAPIContext::GetFormat(AVCodecContext* Context, const AVPixelFormat* PixFmt)
211{
212 while (*PixFmt != AV_PIX_FMT_NONE)
213 {
214 if (*PixFmt == AV_PIX_FMT_VAAPI)
215 if (MythCodecContext::InitialiseDecoder(Context, MythVAAPIContext::InitialiseContext, "VAAPI context creation") >= 0)
216 return AV_PIX_FMT_VAAPI;
217 PixFmt++;
218 }
219 return AV_PIX_FMT_NONE;
220}
221
222AVPixelFormat MythVAAPIContext::GetFormat2(AVCodecContext* Context, const AVPixelFormat* PixFmt)
223{
224 while (*PixFmt != AV_PIX_FMT_NONE)
225 {
226 if (*PixFmt == AV_PIX_FMT_VAAPI)
227 if (InitialiseContext2(Context) >= 0)
228 return AV_PIX_FMT_VAAPI;
229 PixFmt++;
230 }
231 return AV_PIX_FMT_NONE;
232}
233
236int MythVAAPIContext::InitialiseContext(AVCodecContext* Context)
237{
238 if (!Context || !gCoreContext->IsUIThread())
239 return -1;
240
241 // The interop must have a reference to the ui player so it can be deleted
242 // from the main thread.
243 MythVAAPIInterop* interop = nullptr;
244 if (auto * player = GetPlayerUI(Context); player != nullptr)
245 if (auto * render = dynamic_cast<MythRenderOpenGL*>(player->GetRender()); render != nullptr)
246 interop = MythVAAPIInterop::CreateVAAPI(player, render);
247
248 if (!interop || !interop->GetDisplay())
249 {
250 if (interop)
251 interop->DecrRef();
252 return -1;
253 }
254
255 // Create hardware device context
256 auto * hwdeviceref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
257 if (!hwdeviceref || !hwdeviceref->data)
258 {
259 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create VAAPI hardware device context");
260 return -1;
261 }
262
263 AVVAAPIDeviceContext * vaapidevicectx = nullptr;
264 if (auto * hwdevicecontext = reinterpret_cast<AVHWDeviceContext*>(hwdeviceref->data); hwdevicecontext != nullptr)
265 if (vaapidevicectx = reinterpret_cast<AVVAAPIDeviceContext*>(hwdevicecontext->hwctx); !vaapidevicectx)
266 return -1;
267
268 // Set the display
269 vaapidevicectx->display = interop->GetDisplay();
270
271 // Initialise hardware device context
272 int res = av_hwdevice_ctx_init(hwdeviceref);
273 if (res < 0)
274 {
275 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialise VAAPI hardware context");
276 av_buffer_unref(&hwdeviceref);
277 interop->DecrRef();
278 return res;
279 }
280
281 // Allocate the hardware frames context for FFmpeg
282 Context->hw_frames_ctx = av_hwframe_ctx_alloc(hwdeviceref);
283 if (!Context->hw_frames_ctx)
284 {
285 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create VAAPI hardware frames context");
286 av_buffer_unref(&hwdeviceref);
287 interop->DecrRef();
288 return -1;
289 }
290
291 // Setup the frames context
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);
294
295 // Workarounds for specific drivers, surface formats and codecs
296 // NV12 seems to work best across GPUs and codecs with the exception of
297 // MPEG2 on Ironlake where it seems to return I420 labelled as NV12. I420 is
298 // buggy on Sandybridge (stride?) and produces a mixture of I420/NV12 frames
299 // for H.264 on Ironlake.
300 // This may need extending for AMD etc
301
302 auto vendor = interop->GetVendor();
303 // Intel NUC
304 if (vendor.contains("iHD", Qt::CaseInsensitive) && vendor.contains("Intel", Qt::CaseInsensitive))
305 {
306 vaapi_frames_ctx->attributes = nullptr;
307 vaapi_frames_ctx->nb_attributes = 0;
308 }
309 // i965 series
310 else
311 {
312 int format = VA_FOURCC_NV12;
313 if (vendor.contains("ironlake", Qt::CaseInsensitive))
314 if (CODEC_IS_MPEG(Context->codec_id))
315 format = VA_FOURCC_I420;
316
317 if (format != VA_FOURCC_NV12)
318 {
319 auto vaapiid = static_cast<MythCodecID>(kCodec_MPEG1_VAAPI + (mpeg_version(Context->codec_id) - 1));
320 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Forcing surface format for %1 and %2 with driver '%3'")
321 .arg(toString(vaapiid), MythOpenGLInterop::TypeToString(interop->GetType()), vendor));
322 }
323
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;
330 }
331
332 hw_frames_ctx->sw_format = FramesFormat(Context->sw_pix_fmt);
333 int referenceframes = AvFormatDecoder::GetMaxReferenceFrames(Context);
334 hw_frames_ctx->initial_pool_size = static_cast<int>(VideoBuffers::GetNumBuffers(FMT_VAAPI, referenceframes, true));
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;
338 // The frames context now holds the reference to MythVAAPIInterop
339 hw_frames_ctx->user_opaque = interop;
340 // Set the callback to ensure it is released
341 hw_frames_ctx->free = &MythCodecContext::FramesContextFinished;
342
343 // Initialise hardwar frames context
344 res = av_hwframe_ctx_init(Context->hw_frames_ctx);
345 if (res < 0)
346 {
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));
350 return res;
351 }
352
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);
357
359
360 return 0;
361}
362
370int MythVAAPIContext::InitialiseContext2(AVCodecContext* Context)
371{
372 if (!Context)
373 return -1;
374
375 auto * device = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_VAAPI, nullptr,
376 gCoreContext->GetSetting("VAAPIDevice"));
377 Context->hw_frames_ctx = av_hwframe_ctx_alloc(device);
378 if (!Context->hw_frames_ctx)
379 {
380 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create VAAPI hardware frames context");
381 av_buffer_unref(&device);
382 return -1;
383 }
384
385 int referenceframes = AvFormatDecoder::GetMaxReferenceFrames(Context);
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;
392 hw_frames_ctx->initial_pool_size = static_cast<int>(VideoBuffers::GetNumBuffers(FMT_VAAPI, referenceframes));
393 hw_frames_ctx->free = &MythCodecContext::FramesContextFinished;
394 if (av_hwframe_ctx_init(Context->hw_frames_ctx) < 0)
395 {
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));
399 return -1;
400 }
401
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);
406
408
409 return 0;
410}
411
419QString MythVAAPIContext::HaveVAAPI(bool ReCheck /*= false*/)
420{
421 static QString s_vendor;
422 static bool s_checked = false;
423 if (s_checked && !ReCheck)
424 return s_vendor;
425 s_checked = true;
426
427 auto * context = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_VAAPI, nullptr,
428 gCoreContext->GetSetting("VAAPIDevice"));
429 if (context)
430 {
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))
435 {
436 s_vendor = QString();
437 LOG(VB_GENERAL, LOG_INFO, LOC + "VAAPI is using a VDPAU backend - ignoring VAAPI");
438 }
439 else if (s_vendor.isEmpty())
440 {
441 LOG(VB_GENERAL, LOG_INFO, LOC + "Unknown VAAPI vendor - ignoring VAAPI");
442 }
443 else
444 {
445 LOG(VB_GENERAL, LOG_INFO, LOC + "Supported/available VAAPI decoders:");
446 const auto & profiles = MythVAAPIContext::GetProfiles();
447 for (const auto & profile : std::as_const(profiles))
448 {
449 if (profile.first != MythCodecContext::MJPEG)
450 {
451 LOG(VB_GENERAL, LOG_INFO, LOC +
453 }
454 }
455 }
456 av_buffer_unref(&context);
457 }
458 else
459 {
460 LOG(VB_GENERAL, LOG_INFO, LOC + "VAAPI functionality checked failed");
461 }
462
463 return s_vendor;
464}
465
467{
468 static QRecursiveMutex lock;
469 static bool s_initialised = false;
470 static VAAPIProfiles s_profiles;
471
472 QMutexLocker locker(&lock);
473 if (s_initialised)
474 return s_profiles;
475 s_initialised = true;
476
477 auto VAToMythProfile = [](VAProfile Profile)
478 {
479 switch (Profile)
480 {
481 case VAProfileMPEG2Simple: return MythCodecContext::MPEG2Simple;
482 case VAProfileMPEG2Main: return MythCodecContext::MPEG2Main;
483 case VAProfileMPEG4Simple: return MythCodecContext::MPEG4Simple;
484 case VAProfileMPEG4AdvancedSimple: return MythCodecContext::MPEG4AdvancedSimple;
485 case VAProfileMPEG4Main: return MythCodecContext::MPEG4Main;
486 case VAProfileH263Baseline: return MythCodecContext::H263;
487 case VAProfileH264ConstrainedBaseline: return MythCodecContext::H264ConstrainedBaseline;
488 case VAProfileH264Main: return MythCodecContext::H264Main;
489 case VAProfileH264High: return MythCodecContext::H264High;
490 case VAProfileVC1Simple: return MythCodecContext::VC1Simple;
491 case VAProfileVC1Main: return MythCodecContext::VC1Main;
492 case VAProfileVC1Advanced: return MythCodecContext::VC1Advanced;
493 case VAProfileVP8Version0_3: return MythCodecContext::VP8;
494#if VA_CHECK_VERSION(0, 38, 0)
495 case VAProfileVP9Profile0: return MythCodecContext::VP9_0;
496#endif
497#if VA_CHECK_VERSION(0, 39, 0)
498 case VAProfileVP9Profile2: return MythCodecContext::VP9_2;
499#endif
500#if VA_CHECK_VERSION(0, 37, 0)
501 case VAProfileHEVCMain: return MythCodecContext::HEVCMain;
502 case VAProfileHEVCMain10: return MythCodecContext::HEVCMain10;
503#endif
504 case VAProfileJPEGBaseline: return MythCodecContext::MJPEG;
505 default: break;
506 }
508 };
509
510 auto * hwdevicectx = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_VAAPI, nullptr,
511 gCoreContext->GetSetting("VAAPIDevice"));
512 if(!hwdevicectx)
513 return s_profiles;
514
515 auto * device = reinterpret_cast<AVHWDeviceContext*>(hwdevicectx->data);
516 auto * hwctx = reinterpret_cast<AVVAAPIDeviceContext*>(device->hwctx);
517
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)
521 {
522 for (auto i = 0; i < profilecount; ++i)
523 {
524 VAProfile profile = profilelist[i];
525 if (profile == VAProfileNone || profile == VAProfileH264StereoHigh || profile == VAProfileH264MultiviewHigh)
526 continue;
527 int count = 0;
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)
531 {
532 for (int j = 0; j < count; ++j)
533 {
534 if (entrylist[j] != VAEntrypointVLD)
535 continue;
536
537 QSize minsize;
538 QSize maxsize;
539 VAConfigID config = 0;
540 if (vaCreateConfig(hwctx->display, profile, VAEntrypointVLD, nullptr, 0, &config) != VA_STATUS_SUCCESS)
541 continue;
542
543 uint attrcount = 0;
544 if (vaQuerySurfaceAttributes(hwctx->display, config, nullptr, &attrcount) == VA_STATUS_SUCCESS)
545 {
546 auto * attrlist = static_cast<VASurfaceAttrib*>(av_malloc(attrcount * sizeof(VASurfaceAttrib)));
547 if (vaQuerySurfaceAttributes(hwctx->display, config, attrlist, &attrcount) == VA_STATUS_SUCCESS)
548 {
549 for (uint k = 0; k < attrcount; ++k)
550 {
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);
559 }
560 }
561 av_freep(reinterpret_cast<void*>(&attrlist));
562 }
563 vaDestroyConfig(hwctx->display, config);
564 s_profiles.append(VAAPIProfile(VAToMythProfile(profile), QPair<QSize,QSize>(minsize, maxsize)));
565 }
566 }
567 av_freep(reinterpret_cast<void*>(&entrylist));
568 }
569 }
570 av_freep(reinterpret_cast<void*>(&profilelist));
571 av_buffer_unref(&hwdevicectx);
572 return s_profiles;
573}
574
575void MythVAAPIContext::GetDecoderList(QStringList& Decoders)
576{
577 const auto & profiles = MythVAAPIContext::GetProfiles();
578 if (profiles.isEmpty())
579 return;
580 Decoders.append("VAAPI:");
581 for (const auto & profile : std::as_const(profiles))
582 if (profile.first != MythCodecContext::MJPEG)
583 Decoders.append(MythCodecContext::GetProfileDescription(profile.first, profile.second.second));
584}
585
586void MythVAAPIContext::InitVideoCodec(AVCodecContext* Context, bool SelectedStream, bool& DirectRendering)
587{
589 {
590 Context->get_buffer2 = MythCodecContext::GetBuffer;
591 Context->get_format = MythVAAPIContext::GetFormat;
592 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
593 return;
594 }
596 {
597 Context->get_format = MythVAAPIContext::GetFormat2;
598 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
599 DirectRendering = false;
600 return;
601 }
602
603 MythCodecContext::InitVideoCodec(Context, SelectedStream, DirectRendering);
604}
605
606bool MythVAAPIContext::RetrieveFrame(AVCodecContext* /*unused*/, MythVideoFrame* Frame, AVFrame* AvFrame)
607{
608 if (AvFrame->format != AV_PIX_FMT_VAAPI)
609 return false;
611 return RetrieveHWFrame(Frame, AvFrame);
612 return false;
613}
614
624{
625 int ret = 0;
626
627 while (true)
628 {
629 if (m_filterGraph && ((m_filterWidth != Context->coded_width) || (m_filterHeight != Context->coded_height)))
630 {
631 LOG(VB_GENERAL, LOG_WARNING, LOC + "Input changed - deleting filter");
633 }
634
635 if (m_filterGraph)
636 {
637 ret = av_buffersink_get_frame(m_filterSink, Frame);
638 if (ret >= 0)
639 {
641 {
642 Frame->pts = m_filterPriorPTS[1] + ((m_filterPriorPTS[1] - m_filterPriorPTS[0]) / 2);
643 av_frame_remove_side_data(Frame, AV_FRAME_DATA_A53_CC);
644 }
645 else
646 {
647 Frame->pts = m_filterPriorPTS[1];
649 }
650 }
651 if (ret != AVERROR(EAGAIN))
652 break;
653 }
654
655 // EAGAIN or no filter graph
656 ret = avcodec_receive_frame(Context, Frame);
657 if (ret == 0)
658 {
659 // preserve interlaced flags
660 m_lastInterlaced = (Frame->flags & AV_FRAME_FLAG_INTERLACED) != 0;
661 m_lastTopFieldFirst = (Frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) != 0;
662 }
663
664 if (ret < 0)
665 break;
666
667 // some streams with missing timestamps break frame timing when doublerate
668 // so replace with dts if available
669 int64_t pts = Frame->pts;
670 if (pts == AV_NOPTS_VALUE && Frame->pkt_dts != AV_NOPTS_VALUE && m_deinterlacer2x)
671 pts = Frame->pkt_dts;
674
675 if (!m_filterGraph)
676 break;
677
678 ret = av_buffersrc_add_frame(m_filterSource, Frame);
679 if (ret < 0)
680 break;
681 }
682
683 return ret;
684}
685
687{
688 if (!Frame || !codec_is_vaapi_dec(m_codecID) || !Context->hw_frames_ctx)
689 return;
690
691 // if VAAPI driver deints are errored or not available (older boards), then
692 // allow CPU/GLSL
693 if (m_filterError)
694 {
695 Frame->m_deinterlaceAllowed = Frame->m_deinterlaceAllowed & ~DEINT_DRIVER;
696 return;
697 }
700 {
701 // enabling VPP deinterlacing with these codecs breaks decoding for some reason.
702 // HEVC interlacing is not currently detected by FFmpeg and I can't find
703 // any interlaced VP8/9 material. Shaders and/or CPU deints will be available
704 // as appropriate
705 m_filterError = true;
706 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Disabling VAAPI VPP deinterlacer for %1")
707 .arg(toString(m_codecID)));
708 return;
709 }
710
711 // if this frame has already been deinterlaced, then flag the deinterlacer and
712 // that the frame has already been deinterlaced.
713 // the FFmpeg vaapi deint filter will mark frames as progressive, so restore the
714 // interlaced flags to ensure auto deinterlacing continues to work
715 if (m_deinterlacer)
716 {
717 Frame->m_interlaced = m_lastInterlaced;
718 Frame->m_topFieldFirst = m_lastTopFieldFirst;
719 Frame->m_deinterlaceInuse = m_deinterlacer | DEINT_DRIVER;
720 Frame->m_deinterlaceInuse2x = m_deinterlacer2x;
721 Frame->m_alreadyDeinterlaced = true;
722 }
723
724 // N.B. this picks up the scan tracking in MythPlayer. So we can
725 // auto enable deinterlacing etc and override Progressive/Interlaced - but
726 // no reversed interlaced.
727 MythDeintType vaapideint = DEINT_NONE;
728 MythDeintType singlepref = Frame->GetSingleRateOption(DEINT_DRIVER);
729 MythDeintType doublepref = Frame->GetDoubleRateOption(DEINT_DRIVER);
730 bool doublerate = true;
731 bool other = false;
732
733 // For decode only, a CPU or shader deint may also be used/preferred
734 if (doublepref)
735 vaapideint = doublepref;
736 else if (Frame->GetDoubleRateOption(DEINT_CPU | DEINT_SHADER))
737 other = true;
738
739 if (!vaapideint && !other && singlepref)
740 {
741 doublerate = false;
742 vaapideint = singlepref;
743 }
744
745 // nothing to see
746 if (vaapideint == DEINT_NONE)
747 {
748 if (m_deinterlacer)
750 return;
751 }
752
753 // already setup
754 if ((m_deinterlacer == vaapideint) && (m_deinterlacer2x == doublerate))
755 return;
756
757 // Start from scratch
759 m_framesCtx = av_buffer_ref(Context->hw_frames_ctx);
760 if (!MythVAAPIInterop::SetupDeinterlacer(vaapideint, doublerate, Context->hw_frames_ctx,
761 Context->coded_width, Context->coded_height,
763 {
764 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to create deinterlacer %1 - disabling")
765 .arg(MythVideoFrame::DeinterlacerName(vaapideint | DEINT_DRIVER, doublerate, FMT_VAAPI)));
767 m_filterError = true;
768 }
769 else
770 {
771 m_deinterlacer = vaapideint;
772 m_deinterlacer2x = doublerate;
773 m_filterWidth = Context->coded_width;
774 m_filterHeight = Context->coded_height;
775 }
776}
777
778bool MythVAAPIContext::IsDeinterlacing(bool& DoubleRate, bool StreamChange)
779{
780 // the VAAPI deinterlacer can be turned on and off, so on stream changes
781 // return false to ensure auto deint works for the new format (the deinterlacer
782 // refers to the current format)
783 if (m_deinterlacer && !StreamChange)
784 {
785 DoubleRate = m_deinterlacer2x;
786 return true;
787 }
788 DoubleRate = false;
789 return false;
790}
791
793{
794 // HEVC appears to be OK
796}
797
799{
800 // Only MPEG2 tested so far
802}
803
805{
806 if (m_filterGraph)
807 LOG(VB_GENERAL, LOG_INFO, LOC + "Destroying VAAPI deinterlacer");
808 avfilter_graph_free(&m_filterGraph);
809 m_filterGraph = nullptr;
810 m_filterSink = nullptr;
811 m_filterSource = nullptr;
812 m_filterPTSUsed = 0;
813 m_filterPriorPTS.fill(0);
814 m_filterWidth = 0;
815 m_filterHeight = 0;
816 if (m_framesCtx)
817 av_buffer_unref(&m_framesCtx);
819 m_deinterlacer2x = false;
820}
AVFrame AVFrame
static int GetMaxReferenceFrames(AVCodecContext *Context)
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)
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)
MythCodecID m_codecID
static CodecProfile FFmpegToMythProfile(AVCodecID CodecID, int Profile)
QString GetSetting(const QString &key, const QString &defaultval="")
InteropType GetType()
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)
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)
QString GetVendor(void)
static MythVAAPIInterop * CreateVAAPI(MythPlayerUI *Player, MythRenderOpenGL *Context)
VADisplay GetDisplay(void)
static QString DeinterlacerName(MythDeintType Deint, bool DoubleRate, VideoFrameType Format=FMT_NONE)
Definition: mythframe.cpp:462
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
static uint GetNumBuffers(int PixelFormat, int MaxReferenceFrames=16, bool Decoder=false)
unsigned int uint
Definition: freesurround.h:24
uint mpeg_version(AVCodecID codec_id)
MythCodecID
Definition: mythcodecid.h:14
@ kCodec_VP8_VAAPI_DEC
Definition: mythcodecid.h:95
@ kCodec_H264_VAAPI
Definition: mythcodecid.h:76
@ kCodec_MPEG1_VAAPI_DEC
Definition: mythcodecid.h:88
@ kCodec_MPEG1
Definition: mythcodecid.h:24
@ kCodec_MPEG2_VAAPI
Definition: mythcodecid.h:73
@ kCodec_MPEG1_VAAPI
Definition: mythcodecid.h:72
@ kCodec_HEVC_VAAPI_DEC
Definition: mythcodecid.h:97
@ kCodec_VP9_VAAPI_DEC
Definition: mythcodecid.h:96
@ kCodec_MPEG2_VAAPI_DEC
Definition: mythcodecid.h:89
static bool CODEC_IS_MPEG(AVCodecID id)
Definition: mythcodecid.h:386
static bool codec_is_vaapi(MythCodecID id)
Definition: mythcodecid.h:321
static bool codec_is_vaapi_dec(MythCodecID id)
Definition: mythcodecid.h:324
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDeintType
Definition: mythframe.h:67
@ DEINT_DRIVER
Definition: mythframe.h:74
@ DEINT_NONE
Definition: mythframe.h:68
@ DEINT_SHADER
Definition: mythframe.h:73
@ DEINT_CPU
Definition: mythframe.h:72
@ FMT_VAAPI
Definition: mythframe.h:57
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC
QVector< VAAPIProfile > VAAPIProfiles
QPair< MythCodecContext::CodecProfile, QPair< QSize, QSize > > VAAPIProfile
#define VA_FOURCC_I420
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
std::chrono::duration< CHRONO_TYPE, std::ratio< 1, 90000 > > pts
Definition: mythchrono.h:55