From f25e7ebaf7d3c9c777ae47053fc57d23002b1482 Mon Sep 17 00:00:00 2001 From: Josh Rosenberg Date: Mon, 2 Nov 2026 08:24:02 -0500 Subject: [PATCH] VAAPI interop: accept VA surface ID 0 MythVAAPIInterop::VerifySurface() rejected a frame whose m_buffer was null and also returned 0 to signal failure. m_buffer holds the VASurfaceID cast to a pointer, and 0 is a valid surface ID: the Intel iHD driver issues it to the first surface created. FFmpeg preallocates the hardware frame pool in order and returns the frames to its LIFO free list in the same order, so surface 0 sits at the bottom of the list. It is handed out only when every surface is in use at once. When that happens, it stays in rotation and every frame decoded into it is silently dropped. The previous image is held for one extra frame, roughly every 12-15 frames (about 0.4 s for 1080i). With the VAAPI deinterlacer, the gap in frame numbers also triggers the discontinuity check, so the deinterlacer is destroyed and recreated every cycle. Use VA_INVALID_SURFACE as the failure value instead of 0, and stop treating a null m_buffer as an invalid VAAPI frame. Also use VA_INVALID_SURFACE for the "no last filtered frame" state in the deinterlacer, so a filtered surface with ID 0 is handled the same way. Fixes #1530 Co-Authored-By: Claude Opus 5.5 --- .../libmythtv/opengl/mythvaapidrminterop.cpp | 2 +- .../libmythtv/opengl/mythvaapiglxinterop.cpp | 4 ++-- .../libmythtv/opengl/mythvaapiinterop.cpp | 21 ++++++++++++------- .../libs/libmythtv/opengl/mythvaapiinterop.h | 2 +- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/mythtv/libs/libmythtv/opengl/mythvaapidrminterop.cpp b/mythtv/libs/libmythtv/opengl/mythvaapidrminterop.cpp index bed8ee9bb3a..d63ff47c1c1 100644 --- a/mythtv/libs/libmythtv/opengl/mythvaapidrminterop.cpp +++ b/mythtv/libs/libmythtv/opengl/mythvaapidrminterop.cpp @@ -180,7 +180,7 @@ MythVAAPIInteropDRM::Acquire(MythRenderOpenGL* Context, return result; VASurfaceID id = VerifySurface(Context, Frame); - if (!id || !m_vaDisplay) + if ((id == VA_INVALID_SURFACE) || !m_vaDisplay) return result; // Update frame colourspace and initialise on first frame diff --git a/mythtv/libs/libmythtv/opengl/mythvaapiglxinterop.cpp b/mythtv/libs/libmythtv/opengl/mythvaapiglxinterop.cpp index b426dc608fa..72fb676bdb8 100644 --- a/mythtv/libs/libmythtv/opengl/mythvaapiglxinterop.cpp +++ b/mythtv/libs/libmythtv/opengl/mythvaapiglxinterop.cpp @@ -272,7 +272,7 @@ MythVAAPIInteropGLXCopy::Acquire(MythRenderOpenGL* Context, // Retrieve the VASurface VASurfaceID id = VerifySurface(Context, Frame); - if (!id || !m_vaDisplay) + if ((id == VA_INVALID_SURFACE) || !m_vaDisplay) return result; // Initialise colourspace on first frame @@ -370,7 +370,7 @@ MythVAAPIInteropGLXPixmap::Acquire(MythRenderOpenGL* Context, // Retrieve the VASurface VASurfaceID id = VerifySurface(Context, Frame); - if (!id || !m_vaDisplay) + if ((id == VA_INVALID_SURFACE) || !m_vaDisplay) return result; // Initialise colourspace on first frame diff --git a/mythtv/libs/libmythtv/opengl/mythvaapiinterop.cpp b/mythtv/libs/libmythtv/opengl/mythvaapiinterop.cpp index 4fd626b3e63..714fcee74c3 100644 --- a/mythtv/libs/libmythtv/opengl/mythvaapiinterop.cpp +++ b/mythtv/libs/libmythtv/opengl/mythvaapiinterop.cpp @@ -175,19 +175,27 @@ void MythVAAPIInterop::DestroyDeinterlacer(void) m_deinterlacer = DEINT_NONE; m_deinterlacer2x = false; m_firstField = true; - m_lastFilteredFrame = 0; + m_lastFilteredFrame = VA_INVALID_SURFACE; m_lastFilteredFrameCount = 0; av_buffer_unref(&m_vppFramesContext); } +/*! \brief Check that Frame holds a VAAPI surface we can use. + * + * \returns The frame's VASurfaceID, or VA_INVALID_SURFACE on failure. + * + * \note m_buffer holds the VASurfaceID cast to a pointer, and 0 is a valid + * surface ID (the Intel iHD driver issues it), so a null m_buffer must not be + * treated as an error. + */ VASurfaceID MythVAAPIInterop::VerifySurface(MythRenderOpenGL *Context, MythVideoFrame *Frame) { - VASurfaceID result = 0; + VASurfaceID result = VA_INVALID_SURFACE; if (!Frame) return result; if ((Frame->m_pixFmt != AV_PIX_FMT_VAAPI) || (Frame->m_type != FMT_VAAPI) || - !Frame->m_buffer || !Frame->m_priv[1]) + !Frame->m_priv[1]) return result; // Sanity check the context @@ -207,10 +215,7 @@ VASurfaceID MythVAAPIInterop::VerifySurface(MythRenderOpenGL *Context, MythVideo } // Retrieve surface - auto id = static_cast(reinterpret_cast(Frame->m_buffer)); - if (id) - result = id; - return result; + return static_cast(reinterpret_cast(Frame->m_buffer)); } bool MythVAAPIInterop::SetupDeinterlacer(MythDeintType Deinterlacer, bool DoubleRate, @@ -474,7 +479,7 @@ VASurfaceID MythVAAPIInterop::Deinterlace(MythVideoFrame *Frame, VASurfaceID Cur // buffering in the VAAPI frames context, causes the image to 'jiggle' when using // double rate deinterlacing. If we are confident this is a pause frame we have seen, // return the last deinterlaced frame. - if (Frame->m_pauseFrame && m_lastFilteredFrame && (m_lastFilteredFrameCount == Frame->m_frameCounter)) + if (Frame->m_pauseFrame && (m_lastFilteredFrame != VA_INVALID_SURFACE) && (m_lastFilteredFrameCount == Frame->m_frameCounter)) return m_lastFilteredFrame; Frame->m_deinterlaceInuse = m_deinterlacer | DEINT_DRIVER; diff --git a/mythtv/libs/libmythtv/opengl/mythvaapiinterop.h b/mythtv/libs/libmythtv/opengl/mythvaapiinterop.h index e102bb33767..6422b2637f6 100644 --- a/mythtv/libs/libmythtv/opengl/mythvaapiinterop.h +++ b/mythtv/libs/libmythtv/opengl/mythvaapiinterop.h @@ -66,7 +66,7 @@ class MythVAAPIInterop : public MythOpenGLInterop bool m_filterError { false }; int m_filterWidth { 0 }; int m_filterHeight { 0 }; - VASurfaceID m_lastFilteredFrame { 0 }; + VASurfaceID m_lastFilteredFrame { VA_INVALID_SURFACE }; uint64_t m_lastFilteredFrameCount { 0 }; };