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