4#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
5#include <QtEnvironmentVariables>
9#include "libmythbase/mythconfig.h"
29#include "libavfilter/buffersrc.h"
30#include "libavfilter/buffersink.h"
31#include "libavutil/hwcontext_vaapi.h"
34#define LOC QString("VAAPIInterop: ")
58 [[maybe_unused]]
bool egl = Context->
IsEGL();
59 [[maybe_unused]]
bool opengles = Context->isOpenGLES();
60 [[maybe_unused]]
bool wayland = qgetenv(
"XDG_SESSION_TYPE").contains(
"wayland");
70#if CONFIG_VAAPI_DRM && CONFIG_EGL
78 if (!egl && !wayland && MythVAAPIInteropGLXPixmap::IsSupported(Context))
82 if (!egl && !opengles && !wayland)
86 if (!vaapitypes.empty())
98 for ([[maybe_unused]]
auto type : vaapi->second)
100#if CONFIG_VAAPI_DRM && CONFIG_EGL
107 return new MythVAAPIInteropGLXPixmap(
Player, Context);
134 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Error closing VAAPI display");
155 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to initialise VAAPI display");
162 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Created VAAPI %1.%2 display for %3 (%4)")
170 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Destroying VAAPI deinterlacer");
185 VASurfaceID result = 0;
196 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Mismatched OpenGL contexts!");
201 QSize surfacesize(
Frame->m_width,
Frame->m_height);
205 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Video texture size changed!");
210 auto id =
static_cast<VASurfaceID
>(
reinterpret_cast<uintptr_t
>(
Frame->m_buffer));
217 AVBufferRef *FramesContext,
218 int Width,
int Height,
220 AVFilterGraph *&Graph,
222 AVFilterContext *&Sink)
226 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No hardware frames context");
231 QString deinterlacer =
"bob";
233 deinterlacer =
"motion_adaptive";
235 deinterlacer =
"default";
238 QString filters = QString(
"deinterlace_vaapi=mode=%1:rate=%2:auto=0")
239 .arg(deinterlacer, DoubleRate ?
"field" :
"frame");
240 const AVFilter *buffersrc = avfilter_get_by_name(
"buffer");
241 const AVFilter *buffersink = avfilter_get_by_name(
"buffersink");
242 AVFilterInOut *outputs = avfilter_inout_alloc();
243 AVFilterInOut *inputs = avfilter_inout_alloc();
246 auto cleanup_fn = [&](
int *) {
250 avfilter_graph_free(&Graph);
253 avfilter_inout_free(&inputs);
254 avfilter_inout_free(&outputs);
256 std::unique_ptr<int,
decltype(cleanup_fn)>
cleanup { &ret, cleanup_fn };
258 Graph = avfilter_graph_alloc();
259 if (!outputs || !inputs || !Graph)
261 ret = AVERROR(ENOMEM);
266 Source = avfilter_graph_alloc_filter(Graph, buffersrc,
"in");
269 ret = AVERROR(ENOMEM);
270 LOG(VB_GENERAL, LOG_ERR,
"avfilter_graph_alloc_filter() failed to allocate memory.");
274 AVBufferSrcParameters* params = av_buffersrc_parameters_alloc();
275 if (params ==
nullptr)
277 ret = AVERROR(ENOMEM);
278 LOG(VB_GENERAL, LOG_ERR,
"av_buffersrc_parameters_alloc() failed to allocate memory.");
281 params->format = AV_PIX_FMT_VAAPI;
282 params->time_base = {.num=1, .den=1};
283 params->width = Width;
284 params->height = Height;
285 params->hw_frames_ctx = FramesContext;
286 ret = av_buffersrc_parameters_set(
Source, params);
287 av_freep(
reinterpret_cast<void*
>(¶ms));
290 LOG(VB_GENERAL, LOG_ERR,
LOC +
"av_buffersrc_parameters_set failed");
294 ret = avfilter_init_str(
Source,
nullptr);
297 LOG(VB_GENERAL, LOG_ERR,
LOC +
"avfilter_init_str() failed for buffer source");
302 ret = avfilter_graph_create_filter(&Sink, buffersink,
"out",
303 nullptr,
nullptr, Graph);
306 LOG(VB_GENERAL, LOG_ERR,
LOC +
"avfilter_graph_create_filter failed for buffer sink");
321 outputs->name = av_strdup(
"in");
322 outputs->filter_ctx =
Source;
323 outputs->pad_idx = 0;
324 outputs->next =
nullptr;
332 inputs->name = av_strdup(
"out");
333 inputs->filter_ctx = Sink;
335 inputs->next =
nullptr;
337 ret = avfilter_graph_parse_ptr(Graph, filters.toLocal8Bit().constData(),
338 &inputs, &outputs,
nullptr);
341 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"avfilter_graph_parse_ptr failed for %1")
346 ret = avfilter_graph_config(Graph,
nullptr);
349 LOG(VB_GENERAL, LOG_ERR,
LOC +
350 QString(
"VAAPI deinterlacer config failed - '%1' unsupported?").arg(deinterlacer));
354 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Created deinterlacer '%1'")
362 VASurfaceID result = Current;
370 bool doublerate =
true;
378 deinterlacer = doublepref;
382 deinterlacer = singlepref;
393 auto* frames =
reinterpret_cast<AVBufferRef*
>(
Frame->m_priv[1]);
397 AVBufferRef* hwdeviceref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
401 auto* hwdevicecontext =
reinterpret_cast<AVHWDeviceContext*
>(hwdeviceref->data);
402 hwdevicecontext->free = [](AVHWDeviceContext* ) {
LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"VAAPI VPP device context finished"); };
404 auto *vaapidevicectx =
reinterpret_cast<AVVAAPIDeviceContext*
>(hwdevicecontext->hwctx);
407 if (av_hwdevice_ctx_init(hwdeviceref) < 0)
409 av_buffer_unref(&hwdeviceref);
414 AVBufferRef *newframes = av_hwframe_ctx_alloc(hwdeviceref);
418 av_buffer_unref(&hwdeviceref);
422 auto* dstframes =
reinterpret_cast<AVHWFramesContext*
>(newframes->data);
423 auto* srcframes =
reinterpret_cast<AVHWFramesContext*
>(frames->data);
427 static constexpr int kVppPoolSize = 2;
428 dstframes->sw_format = srcframes->sw_format;
431 dstframes->initial_pool_size = kVppPoolSize;
432 dstframes->format = AV_PIX_FMT_VAAPI;
433 dstframes->free = [](AVHWFramesContext* ) {
LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"VAAPI VPP frames context finished"); };
435 if (av_hwframe_ctx_init(newframes) < 0)
438 av_buffer_unref(&hwdeviceref);
439 av_buffer_unref(&newframes);
444 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"New VAAPI frame pool with %1 %2x%3 surfaces")
446 av_buffer_unref(&hwdeviceref);
452 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to create VAAPI deinterlacer %1 - disabling")
489 while ((result == Current) && retries--)
495 sinkframe->format = AV_PIX_FMT_VAAPI;
504 result =
m_lastFilteredFrame =
static_cast<VASurfaceID
>(
reinterpret_cast<uintptr_t
>(sinkframe->data[3]));
509 if (ret != AVERROR(EAGAIN))
515 sourceframe->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST;
516 if (
Frame->m_interlacedReverse ^
Frame->m_topFieldFirst)
518 sourceframe->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST;
520 sourceframe->flags |= AV_FRAME_FLAG_INTERLACED;
521 sourceframe->data[3] =
Frame->m_buffer;
522 auto* buffer =
reinterpret_cast<AVBufferRef*
>(
Frame->m_priv[0]);
523 sourceframe->buf[0] = buffer ? av_buffer_ref(buffer) :
nullptr;
526 sourceframe->format = AV_PIX_FMT_VAAPI;
532 sourceframe->data[3] =
nullptr;
533 sourceframe->buf[0] =
nullptr;
542 result =
m_lastFilteredFrame =
static_cast<VASurfaceID
>(
reinterpret_cast<uintptr_t
>(sinkframe->data[3]));
MythAVFrame little utility class that act as a safe way to allocate an AVFrame which can then be allo...
static bool DirectRenderingAvailable()
static QString TypeToString(InteropType Type)
std::vector< InteropType > InteropTypes
std::map< VideoFrameType, InteropTypes > InteropMap
MythRenderOpenGL * m_openglContext
static bool IsSupported(MythRenderOpenGL *Context)
virtual void DestroyDeinterlacer(void)
AVBufferRef * m_vppFramesContext
static bool SetupDeinterlacer(MythDeintType Deinterlacer, bool DoubleRate, AVBufferRef *FramesContext, int Width, int Height, AVFilterGraph *&Graph, AVFilterContext *&Source, AVFilterContext *&Sink)
void InitaliseDisplay(void)
~MythVAAPIInterop() override
VASurfaceID m_lastFilteredFrame
virtual void PostInitDeinterlacer(void)
static void GetVAAPITypes(MythRenderOpenGL *Context, MythInteropGPU::InteropMap &Types)
Return a list of interops that are supported by the current render device.
MythDeintType m_deinterlacer
AVFilterGraph * m_filterGraph
static MythVAAPIInterop * CreateVAAPI(MythPlayerUI *Player, MythRenderOpenGL *Context)
AVFilterContext * m_filterSource
VASurfaceID VerifySurface(MythRenderOpenGL *Context, MythVideoFrame *Frame)
uint64_t m_lastFilteredFrameCount
VASurfaceID Deinterlace(MythVideoFrame *Frame, VASurfaceID Current, FrameScanType Scan)
AVFilterContext * m_filterSink
MythVAAPIInterop(MythPlayerUI *Player, MythRenderOpenGL *Context, InteropType Type)
VADisplay GetDisplay(void)
static QString DeinterlacerName(MythDeintType Deint, bool DoubleRate, VideoFrameType Format=FMT_NONE)
static const struct wl_interface * types[]
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
static QString Source(const QNetworkRequest &request)
static QString cleanup(const QString &str)
bool is_interlaced(FrameScanType Scan)