MythTV master
mythvaapicontext.cpp
Go to the documentation of this file.
1// C++ headers
2#include <algorithm>
3
4// Qt
5#include <QCoreApplication>
6#include <QWaitCondition>
7
8// Mythtv
13
15#include "mythplayerui.h"
16#include "mythvaapicontext.h"
18#include "videobuffers.h"
19
20extern "C" {
21#include "libavcodec/defs.h"
22#include "libavutil/hwcontext_vaapi.h"
23#include "libavutil/pixdesc.h"
24#include "libavfilter/buffersink.h"
25}
26
27#define LOC QString("VAAPIDec: ")
28
38 : MythCodecContext(Parent, CodecID)
39{
40}
41
43{
45}
46
47VAProfile MythVAAPIContext::VAAPIProfileForCodec(const AVCodecContext* Codec)
48{
49 if (!Codec)
50 return VAProfileNone;
51
52 switch (Codec->codec_id)
53 {
54 case AV_CODEC_ID_MPEG2VIDEO:
55 switch (Codec->profile)
56 {
57 case AV_PROFILE_MPEG2_SIMPLE: return VAProfileMPEG2Simple;
58 case AV_PROFILE_MPEG2_MAIN: return VAProfileMPEG2Main;
59 default: break;
60 }
61 break;
62 case AV_CODEC_ID_H263: return VAProfileH263Baseline;
63 case AV_CODEC_ID_MPEG4:
64 switch (Codec->profile)
65 {
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;
69 default: break;
70 }
71 break;
72 case AV_CODEC_ID_H264:
73 switch (Codec->profile)
74 {
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;
78 default: break;
79 }
80 break;
81 case AV_CODEC_ID_HEVC:
82 switch (Codec->profile)
83 {
84 case AV_PROFILE_HEVC_MAIN: return VAProfileHEVCMain;
85 case AV_PROFILE_HEVC_MAIN_10: return VAProfileHEVCMain10;
86 default: break;
87 }
88 break;
89 case AV_CODEC_ID_MJPEG: return VAProfileJPEGBaseline;
90 case AV_CODEC_ID_WMV3:
91 case AV_CODEC_ID_VC1:
92 switch (Codec->profile)
93 {
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;
98 default: break;
99 }
100 break;
101 case AV_CODEC_ID_VP8: return VAProfileVP8Version0_3;
102 case AV_CODEC_ID_VP9:
103 switch (Codec->profile)
104 {
105 case AV_PROFILE_VP9_0: return VAProfileVP9Profile0;
106 case AV_PROFILE_VP9_2: return VAProfileVP9Profile2;
107 default: break;
108 }
109 break;
110 default: break;
111 }
112
113 return VAProfileNone;
114}
115
116inline AVPixelFormat MythVAAPIContext::FramesFormat(AVPixelFormat Format)
117{
118 switch (Format)
119 {
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;
125 }
126}
127
131 const AVCodec** /*Codec*/,
132 const QString& Decoder,
133 uint StreamType)
134{
135 bool decodeonly = Decoder == "vaapi-dec";
136 auto success = static_cast<MythCodecID>((decodeonly ? kCodec_MPEG1_VAAPI_DEC : kCodec_MPEG1_VAAPI) + (StreamType - 1));
137 auto failure = static_cast<MythCodecID>(kCodec_MPEG1 + (StreamType - 1));
138 auto vendor = HaveVAAPI();
139 if (!Decoder.startsWith("vaapi") || vendor.isEmpty() || qEnvironmentVariableIsSet("NO_VAAPI"))
140 return failure;
141
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);
145
146 // Simple check for known profile
147 auto desired = VAAPIProfileForCodec(*Context);
148 if (desired == VAProfileNone)
149 {
150 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VAAPI does not support decoding '%1 %2 %3'")
151 .arg(codec, profile, pixfmt));
152 return failure;
153 }
154
155 // Check for ironlake decode only - which won't work due to FFmpeg frame format
156 // constraints. May apply to other platforms.
157 if (decodeonly)
158 {
159 if (vendor.contains("ironlake", Qt::CaseInsensitive))
160 {
161 LOG(VB_GENERAL, LOG_WARNING, LOC + "Disallowing VAAPI decode only for Ironlake");
162 return failure;
163 }
164 }
165 else
166 {
167 if (!FrameTypeIsSupported(*Context, FMT_VAAPI))
168 return failure;
169 }
170
171 // Check profile support
172 bool ok = false;
173 const auto & profiles = MythVAAPIContext::GetProfiles();
174 auto mythprofile = MythCodecContext::FFmpegToMythProfile((*Context)->codec_id, (*Context)->profile);
175 auto haveprofile = [&](MythCodecContext::CodecProfile Profile, QSize Size)
176 {
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(); } );
184 };
185
186 ok = haveprofile(mythprofile, QSize((*Context)->width, (*Context)->height));
187 // use JPEG support as a proxy for MJPEG (full range YUV)
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))
190 {
191 ok = haveprofile(MythCodecContext::MJPEG, QSize());
192 }
193
194 auto desc = QString("'%1 %2 %3 %4x%5'").arg(codec, profile, pixfmt)
195 .arg((*Context)->width).arg((*Context)->height);
196
197 if (ok)
198 {
199 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VAAPI supports decoding %1").arg(desc));
200 (*Context)->pix_fmt = AV_PIX_FMT_VAAPI;
201 return success;
202 }
203
204 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("VAAPI does NOT support %1").arg(desc));
205 return failure;
206}
207
208AVPixelFormat MythVAAPIContext::GetFormat(AVCodecContext* Context, const AVPixelFormat* PixFmt)
209{
210 while (*PixFmt != AV_PIX_FMT_NONE)
211 {
212 if (*PixFmt == AV_PIX_FMT_VAAPI)
213 if (MythCodecContext::InitialiseDecoder(Context, MythVAAPIContext::InitialiseContext, "VAAPI context creation") >= 0)
214 return AV_PIX_FMT_VAAPI;
215 PixFmt++;
216 }
217 return AV_PIX_FMT_NONE;
218}
219
220AVPixelFormat MythVAAPIContext::GetFormat2(AVCodecContext* Context, const AVPixelFormat* PixFmt)
221{
222 while (*PixFmt != AV_PIX_FMT_NONE)
223 {
224 if (*PixFmt == AV_PIX_FMT_VAAPI)
225 if (InitialiseContext2(Context) >= 0)
226 return AV_PIX_FMT_VAAPI;
227 PixFmt++;
228 }
229 return AV_PIX_FMT_NONE;
230}
231
234int MythVAAPIContext::InitialiseContext(AVCodecContext* Context)
235{
236 if (!Context || !gCoreContext->IsUIThread())
237 return -1;
238
239 // The interop must have a reference to the ui player so it can be deleted
240 // from the main thread.
241 MythVAAPIInterop* interop = nullptr;
242 if (auto * player = GetPlayerUI(Context); player != nullptr)
243 if (auto * render = dynamic_cast<MythRenderOpenGL*>(player->GetRender()); render != nullptr)
244 interop = MythVAAPIInterop::CreateVAAPI(player, render);
245
246 if (!interop || !interop->GetDisplay())
247 {
248 if (interop)
249 interop->DecrRef();
250 return -1;
251 }
252
253 // Create hardware device context
254 auto * hwdeviceref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
255 if (!hwdeviceref || !hwdeviceref->data)
256 {
257 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create VAAPI hardware device context");
258 return -1;
259 }
260
261 AVVAAPIDeviceContext * vaapidevicectx = nullptr;
262 if (auto * hwdevicecontext = reinterpret_cast<AVHWDeviceContext*>(hwdeviceref->data); hwdevicecontext != nullptr)
263 if (vaapidevicectx = reinterpret_cast<AVVAAPIDeviceContext*>(hwdevicecontext->hwctx); !vaapidevicectx)
264 return -1;
265
266 // Set the display
267 vaapidevicectx->display = interop->GetDisplay();
268
269 // Initialise hardware device context
270 int res = av_hwdevice_ctx_init(hwdeviceref);
271 if (res < 0)
272 {
273 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialise VAAPI hardware context");
274 av_buffer_unref(&hwdeviceref);
275 interop->DecrRef();
276 return res;
277 }
278
279 // Allocate the hardware frames context for FFmpeg
280 Context->hw_frames_ctx = av_hwframe_ctx_alloc(hwdeviceref);
281 if (!Context->hw_frames_ctx)
282 {
283 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create VAAPI hardware frames context");
284 av_buffer_unref(&hwdeviceref);
285 interop->DecrRef();
286 return -1;
287 }
288
289 // Setup the frames context
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);
292
293 // Workarounds for specific drivers, surface formats and codecs
294 // NV12 seems to work best across GPUs and codecs with the exception of
295 // MPEG2 on Ironlake where it seems to return I420 labelled as NV12. I420 is
296 // buggy on Sandybridge (stride?) and produces a mixture of I420/NV12 frames
297 // for H.264 on Ironlake.
298 // This may need extending for AMD etc
299
300 auto vendor = interop->GetVendor();
301 if (vendor.contains("i965", Qt::CaseInsensitive))
302 {
303 int format = VA_FOURCC_NV12;
304 if (vendor.contains("ironlake", Qt::CaseInsensitive))
305 if (CODEC_IS_MPEG(Context->codec_id))
306 format = VA_FOURCC_I420;
307
308 if (format != VA_FOURCC_NV12)
309 {
310 auto vaapiid = static_cast<MythCodecID>(kCodec_MPEG1_VAAPI + (mpeg_version(Context->codec_id) - 1));
311 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Forcing surface format for %1 and %2 with driver '%3'")
312 .arg(toString(vaapiid), MythOpenGLInterop::TypeToString(interop->GetType()), vendor));
313 }
314
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;
327 }
328
329 hw_frames_ctx->sw_format = FramesFormat(Context->sw_pix_fmt);
330 int referenceframes = AvFormatDecoder::GetMaxReferenceFrames(Context);
331 hw_frames_ctx->initial_pool_size = static_cast<int>(VideoBuffers::GetNumBuffers(FMT_VAAPI, referenceframes, true));
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;
335 // The frames context now holds the reference to MythVAAPIInterop
336 hw_frames_ctx->user_opaque = interop;
337 // Set the callback to ensure it is released
338 hw_frames_ctx->free = &MythCodecContext::FramesContextFinished;
339
340 // Initialise hardwar frames context
341 res = av_hwframe_ctx_init(Context->hw_frames_ctx);
342 if (res < 0)
343 {
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));
347 return res;
348 }
349
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);
354
356
357 return 0;
358}
359
367int MythVAAPIContext::InitialiseContext2(AVCodecContext* Context)
368{
369 if (!Context)
370 return -1;
371
372 auto * device = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_VAAPI, nullptr,
373 gCoreContext->GetSetting("VAAPIDevice"));
374 Context->hw_frames_ctx = av_hwframe_ctx_alloc(device);
375 if (!Context->hw_frames_ctx)
376 {
377 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create VAAPI hardware frames context");
378 av_buffer_unref(&device);
379 return -1;
380 }
381
382 int referenceframes = AvFormatDecoder::GetMaxReferenceFrames(Context);
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;
389 hw_frames_ctx->initial_pool_size = static_cast<int>(VideoBuffers::GetNumBuffers(FMT_VAAPI, referenceframes));
390 hw_frames_ctx->free = &MythCodecContext::FramesContextFinished;
391 if (av_hwframe_ctx_init(Context->hw_frames_ctx) < 0)
392 {
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));
396 return -1;
397 }
398
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);
403
405
406 return 0;
407}
408
416QString MythVAAPIContext::HaveVAAPI(bool ReCheck /*= false*/)
417{
418 static QString s_vendor;
419 static bool s_checked = false;
420 if (s_checked && !ReCheck)
421 return s_vendor;
422 s_checked = true;
423
424 auto * context = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_VAAPI, nullptr,
425 gCoreContext->GetSetting("VAAPIDevice"));
426 if (context)
427 {
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))
432 {
433 s_vendor = QString();
434 LOG(VB_GENERAL, LOG_INFO, LOC + "VAAPI is using a VDPAU backend - ignoring VAAPI");
435 }
436 else if (s_vendor.isEmpty())
437 {
438 LOG(VB_GENERAL, LOG_INFO, LOC + "Unknown VAAPI vendor - ignoring VAAPI");
439 }
440 else
441 {
442 LOG(VB_GENERAL, LOG_INFO, LOC + "Supported/available VAAPI decoders:");
443 const auto & profiles = MythVAAPIContext::GetProfiles();
444 for (const auto & profile : std::as_const(profiles))
445 {
446 if (profile.first != MythCodecContext::MJPEG)
447 {
448 LOG(VB_GENERAL, LOG_INFO, LOC +
450 }
451 }
452 }
453 av_buffer_unref(&context);
454 }
455 else
456 {
457 LOG(VB_GENERAL, LOG_INFO, LOC + "VAAPI functionality checked failed");
458 }
459
460 return s_vendor;
461}
462
464{
465 static QRecursiveMutex lock;
466 static bool s_initialised = false;
467 static VAAPIProfiles s_profiles;
468
469 QMutexLocker locker(&lock);
470 if (s_initialised)
471 return s_profiles;
472 s_initialised = true;
473
474 auto VAToMythProfile = [](VAProfile Profile)
475 {
476 switch (Profile)
477 {
478 case VAProfileMPEG2Simple: return MythCodecContext::MPEG2Simple;
479 case VAProfileMPEG2Main: return MythCodecContext::MPEG2Main;
480 case VAProfileMPEG4Simple: return MythCodecContext::MPEG4Simple;
481 case VAProfileMPEG4AdvancedSimple: return MythCodecContext::MPEG4AdvancedSimple;
482 case VAProfileMPEG4Main: return MythCodecContext::MPEG4Main;
483 case VAProfileH263Baseline: return MythCodecContext::H263;
484 case VAProfileH264ConstrainedBaseline: return MythCodecContext::H264ConstrainedBaseline;
485 case VAProfileH264Main: return MythCodecContext::H264Main;
486 case VAProfileH264High: return MythCodecContext::H264High;
487 case VAProfileVC1Simple: return MythCodecContext::VC1Simple;
488 case VAProfileVC1Main: return MythCodecContext::VC1Main;
489 case VAProfileVC1Advanced: return MythCodecContext::VC1Advanced;
490 case VAProfileVP8Version0_3: return MythCodecContext::VP8;
491 case VAProfileVP9Profile0: return MythCodecContext::VP9_0;
492 case VAProfileVP9Profile2: return MythCodecContext::VP9_2;
493 case VAProfileHEVCMain: return MythCodecContext::HEVCMain;
494 case VAProfileHEVCMain10: return MythCodecContext::HEVCMain10;
495 case VAProfileJPEGBaseline: return MythCodecContext::MJPEG;
496 default: break;
497 }
499 };
500
501 auto * hwdevicectx = MythCodecContext::CreateDevice(AV_HWDEVICE_TYPE_VAAPI, nullptr,
502 gCoreContext->GetSetting("VAAPIDevice"));
503 if(!hwdevicectx)
504 return s_profiles;
505
506 auto * device = reinterpret_cast<AVHWDeviceContext*>(hwdevicectx->data);
507 auto * hwctx = reinterpret_cast<AVVAAPIDeviceContext*>(device->hwctx);
508
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)
512 {
513 for (auto i = 0; i < profilecount; ++i)
514 {
515 VAProfile profile = profilelist[i];
516 if (profile == VAProfileNone || profile == VAProfileH264StereoHigh || profile == VAProfileH264MultiviewHigh)
517 continue;
518 int count = 0;
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)
522 {
523 for (int j = 0; j < count; ++j)
524 {
525 if (entrylist[j] != VAEntrypointVLD)
526 continue;
527
528 QSize minsize;
529 QSize maxsize;
530 VAConfigID config = 0;
531 if (vaCreateConfig(hwctx->display, profile, VAEntrypointVLD, nullptr, 0, &config) != VA_STATUS_SUCCESS)
532 continue;
533
534 uint attrcount = 0;
535 if (vaQuerySurfaceAttributes(hwctx->display, config, nullptr, &attrcount) == VA_STATUS_SUCCESS)
536 {
537 auto * attrlist = static_cast<VASurfaceAttrib*>(av_malloc(attrcount * sizeof(VASurfaceAttrib)));
538 if (vaQuerySurfaceAttributes(hwctx->display, config, attrlist, &attrcount) == VA_STATUS_SUCCESS)
539 {
540 for (uint k = 0; k < attrcount; ++k)
541 {
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);
550 }
551 }
552 av_freep(reinterpret_cast<void*>(&attrlist));
553 }
554 vaDestroyConfig(hwctx->display, config);
555 s_profiles.append(VAAPIProfile(VAToMythProfile(profile), QPair<QSize,QSize>(minsize, maxsize)));
556 }
557 }
558 av_freep(reinterpret_cast<void*>(&entrylist));
559 }
560 }
561 av_freep(reinterpret_cast<void*>(&profilelist));
562 av_buffer_unref(&hwdevicectx);
563 return s_profiles;
564}
565
566void MythVAAPIContext::GetDecoderList(QStringList& Decoders)
567{
568 const auto & profiles = MythVAAPIContext::GetProfiles();
569 if (profiles.isEmpty())
570 return;
571 Decoders.append("VAAPI:");
572 for (const auto & profile : std::as_const(profiles))
573 if (profile.first != MythCodecContext::MJPEG)
574 Decoders.append(MythCodecContext::GetProfileDescription(profile.first, profile.second.second));
575}
576
577void MythVAAPIContext::InitVideoCodec(AVCodecContext* Context, bool SelectedStream, bool& DirectRendering)
578{
580 {
581 Context->get_buffer2 = MythCodecContext::GetBuffer;
582 Context->get_format = MythVAAPIContext::GetFormat;
583 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
584 return;
585 }
587 {
588 Context->get_format = MythVAAPIContext::GetFormat2;
589 Context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
590 DirectRendering = false;
591 return;
592 }
593
594 MythCodecContext::InitVideoCodec(Context, SelectedStream, DirectRendering);
595}
596
597bool MythVAAPIContext::RetrieveFrame(AVCodecContext* /*unused*/, MythVideoFrame* Frame, AVFrame* AvFrame)
598{
599 if (AvFrame->format != AV_PIX_FMT_VAAPI)
600 return false;
602 return RetrieveHWFrame(Frame, AvFrame);
603 return false;
604}
605
615{
616 int ret = 0;
617
618 while (true)
619 {
620 if (m_filterGraph && ((m_filterWidth != Context->coded_width) || (m_filterHeight != Context->coded_height)))
621 {
622 LOG(VB_GENERAL, LOG_WARNING, LOC + "Input changed - deleting filter");
624 }
625
626 if (m_filterGraph)
627 {
628 ret = av_buffersink_get_frame(m_filterSink, Frame);
629 if (ret >= 0)
630 {
632 {
633 Frame->pts = m_filterPriorPTS[1] + ((m_filterPriorPTS[1] - m_filterPriorPTS[0]) / 2);
634 av_frame_remove_side_data(Frame, AV_FRAME_DATA_A53_CC);
635 }
636 else
637 {
638 Frame->pts = m_filterPriorPTS[1];
640 }
641 }
642 if (ret != AVERROR(EAGAIN))
643 break;
644 }
645
646 // EAGAIN or no filter graph
647 ret = avcodec_receive_frame(Context, Frame);
648 if (ret == 0)
649 {
650 // preserve interlaced flags
651 m_lastInterlaced = (Frame->flags & AV_FRAME_FLAG_INTERLACED) != 0;
652 m_lastTopFieldFirst = (Frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) != 0;
653 }
654
655 if (ret < 0)
656 break;
657
658 // some streams with missing timestamps break frame timing when doublerate
659 // so replace with dts if available
660 int64_t pts = Frame->pts;
661 if (pts == AV_NOPTS_VALUE && Frame->pkt_dts != AV_NOPTS_VALUE && m_deinterlacer2x)
662 pts = Frame->pkt_dts;
665
666 if (!m_filterGraph)
667 break;
668
669 ret = av_buffersrc_add_frame(m_filterSource, Frame);
670 if (ret < 0)
671 break;
672 }
673
674 return ret;
675}
676
678{
679 if (!Frame || !codec_is_vaapi_dec(m_codecID) || !Context->hw_frames_ctx)
680 return;
681
682 // if VAAPI driver deints are errored or not available (older boards), then
683 // allow CPU/GLSL
684 if (m_filterError)
685 {
686 Frame->m_deinterlaceAllowed = Frame->m_deinterlaceAllowed & ~DEINT_DRIVER;
687 return;
688 }
691 {
692 // enabling VPP deinterlacing with these codecs breaks decoding for some reason.
693 // HEVC interlacing is not currently detected by FFmpeg and I can't find
694 // any interlaced VP8/9 material. Shaders and/or CPU deints will be available
695 // as appropriate
696 m_filterError = true;
697 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Disabling VAAPI VPP deinterlacer for %1")
698 .arg(toString(m_codecID)));
699 return;
700 }
701
702 // if this frame has already been deinterlaced, then flag the deinterlacer and
703 // that the frame has already been deinterlaced.
704 // the FFmpeg vaapi deint filter will mark frames as progressive, so restore the
705 // interlaced flags to ensure auto deinterlacing continues to work
706 if (m_deinterlacer)
707 {
708 Frame->m_interlaced = m_lastInterlaced;
709 Frame->m_topFieldFirst = m_lastTopFieldFirst;
710 Frame->m_deinterlaceInuse = m_deinterlacer | DEINT_DRIVER;
711 Frame->m_deinterlaceInuse2x = m_deinterlacer2x;
712 Frame->m_alreadyDeinterlaced = true;
713 }
714
715 // N.B. this picks up the scan tracking in MythPlayer. So we can
716 // auto enable deinterlacing etc and override Progressive/Interlaced - but
717 // no reversed interlaced.
718 MythDeintType vaapideint = DEINT_NONE;
719 MythDeintType singlepref = Frame->GetSingleRateOption(DEINT_DRIVER);
720 MythDeintType doublepref = Frame->GetDoubleRateOption(DEINT_DRIVER);
721 bool doublerate = true;
722 bool other = false;
723
724 // For decode only, a CPU or shader deint may also be used/preferred
725 if (doublepref)
726 vaapideint = doublepref;
727 else if (Frame->GetDoubleRateOption(DEINT_CPU | DEINT_SHADER))
728 other = true;
729
730 if (!vaapideint && !other && singlepref)
731 {
732 doublerate = false;
733 vaapideint = singlepref;
734 }
735
736 // nothing to see
737 if (vaapideint == DEINT_NONE)
738 {
739 if (m_deinterlacer)
741 return;
742 }
743
744 // already setup
745 if ((m_deinterlacer == vaapideint) && (m_deinterlacer2x == doublerate))
746 return;
747
748 // Start from scratch
750 m_framesCtx = av_buffer_ref(Context->hw_frames_ctx);
751 if (!MythVAAPIInterop::SetupDeinterlacer(vaapideint, doublerate, Context->hw_frames_ctx,
752 Context->coded_width, Context->coded_height,
754 {
755 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to create deinterlacer %1 - disabling")
756 .arg(MythVideoFrame::DeinterlacerName(vaapideint | DEINT_DRIVER, doublerate, FMT_VAAPI)));
758 m_filterError = true;
759 }
760 else
761 {
762 m_deinterlacer = vaapideint;
763 m_deinterlacer2x = doublerate;
764 m_filterWidth = Context->coded_width;
765 m_filterHeight = Context->coded_height;
766 }
767}
768
769bool MythVAAPIContext::IsDeinterlacing(bool& DoubleRate, bool StreamChange)
770{
771 // the VAAPI deinterlacer can be turned on and off, so on stream changes
772 // return false to ensure auto deint works for the new format (the deinterlacer
773 // refers to the current format)
774 if (m_deinterlacer && !StreamChange)
775 {
776 DoubleRate = m_deinterlacer2x;
777 return true;
778 }
779 DoubleRate = false;
780 return false;
781}
782
784{
785 // HEVC appears to be OK
787}
788
790{
791 // Only MPEG2 tested so far
793}
794
796{
797 if (m_filterGraph)
798 LOG(VB_GENERAL, LOG_INFO, LOC + "Destroying VAAPI deinterlacer");
799 avfilter_graph_free(&m_filterGraph);
800 m_filterGraph = nullptr;
801 m_filterSink = nullptr;
802 m_filterSource = nullptr;
803 m_filterPTSUsed = 0;
804 m_filterPriorPTS.fill(0);
805 m_filterWidth = 0;
806 m_filterHeight = 0;
807 if (m_framesCtx)
808 av_buffer_unref(&m_framesCtx);
810 m_deinterlacer2x = false;
811}
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:466
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: compat.h:60
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:44