Reported from use with a screenshot: many movie/TV streams play as dense coloured speckle noise with
horizontal streaking over a dark image. Subtitles render perfectly on top, so the render path is fine —
the decoded frames are corrupt. It affects many streams, so it is systematic, not the content.
What the log says the player actually did
16:07:44 mpv: hwdec requested='auto-safe' (setting=auto)
16:10:17 video: codec='H.264 / AVC …' 1920x798 pixfmt=cuda hwdec-current=nvdec
16:20:47 mpv: hwdec requested='auto-safe' (setting=auto)
16:21:31 video: codec='H.265 / HEVC …' 1620x1080 pixfmt=cuda hwdec-current=nvdec
hwdec-current=nvdec, pixfmt=cuda, on both an H.264 and an HEVC stream. That is nvdec direct: CUDA
frames handed to OpenGL through CUDA↔GL interop, not copied back to system memory.
The design assumption this contradicts
HwDecode.h and Settings.h document why auto is the default:
Auto = "auto-safe" (copy-back preferred, software fallback) is deliberately the default — it sidesteps
the D3D11VA 10-bit-HEVC corruption that made a blanket "no" the original hard-coded choice.
The intent is clearly copy-back. But mpv's auto-safe is a whitelist of decoders mpv considers safe,
and on an NVIDIA machine that whitelist includes nvdec direct — so auto-safe does not deliver
copy-back here. It delivers the interop path, and the interop path is what is corrupting frames.
This is a different failure from the one the comment remembers. That one was D3D11VA on 10-bit HEVC.
This one is nvdec interop on ordinary 8-bit H.264 and HEVC, through a Qt QOpenGLWidget context
(MPV_RENDER_API_TYPE_OPENGL, MpvWidget.cpp).
Corroborating evidence
crash_report.log, 2026-08-25 16:12: a fatal 0xc0000005 inside nvoglv64.dll (the NVIDIA OpenGL
driver) on a driver thread — the same GL path under load.
- The themed UI is deliberately software-rendered because of "an mpv GL conflict" (documented in
main.cpp). GL sharing with mpv on this machine has form.
The fix to establish, not assume
mpv has hwdec=auto-copy-safe — the same safe whitelist, copy-back variants only — which is what
the design comment describes wanting. Mapping the auto choice to auto-copy-safe instead of
auto-safe would deliver the documented intent. Costs one frame copy per frame; on modern hardware
that is not a visible cost at these resolutions, and it removes the interop entirely.
Establish it empirically before shipping: the same stream under hwdec=nvdec (expect corruption),
nvdec-copy (expect clean), no (expect clean, baseline), and auto-copy-safe (expect clean and
hwdec-current=nvdec-copy). The log line at MpvWidget.cpp:503 already prints hwdec-current, so the
evidence is one grep per run.
Also check what auto-copy-safe resolves to on a machine without NVIDIA — the fix must not regress
D3D11VA-copy or Android's mediacodec-copy, both of which HwDecode.h reasons about explicitly.
Immediate workaround for a user
Settings → Video → hardware decoding → Off (video/hwdec=off). Guaranteed-correct software decode,
higher CPU. Flipping it and watching the corruption stop is also the cheapest confirmation of this
diagnosis.
Reported from use with a screenshot: many movie/TV streams play as dense coloured speckle noise with
horizontal streaking over a dark image. Subtitles render perfectly on top, so the render path is fine —
the decoded frames are corrupt. It affects many streams, so it is systematic, not the content.
What the log says the player actually did
hwdec-current=nvdec,pixfmt=cuda, on both an H.264 and an HEVC stream. That is nvdec direct: CUDAframes handed to OpenGL through CUDA↔GL interop, not copied back to system memory.
The design assumption this contradicts
HwDecode.handSettings.hdocument whyautois the default:The intent is clearly copy-back. But mpv's
auto-safeis a whitelist of decoders mpv considers safe,and on an NVIDIA machine that whitelist includes
nvdecdirect — soauto-safedoes not delivercopy-back here. It delivers the interop path, and the interop path is what is corrupting frames.
This is a different failure from the one the comment remembers. That one was D3D11VA on 10-bit HEVC.
This one is nvdec interop on ordinary 8-bit H.264 and HEVC, through a Qt
QOpenGLWidgetcontext(
MPV_RENDER_API_TYPE_OPENGL,MpvWidget.cpp).Corroborating evidence
crash_report.log, 2026-08-25 16:12: a fatal0xc0000005insidenvoglv64.dll(the NVIDIA OpenGLdriver) on a driver thread — the same GL path under load.
main.cpp). GL sharing with mpv on this machine has form.The fix to establish, not assume
mpv has
hwdec=auto-copy-safe— the same safe whitelist, copy-back variants only — which is whatthe design comment describes wanting. Mapping the
autochoice toauto-copy-safeinstead ofauto-safewould deliver the documented intent. Costs one frame copy per frame; on modern hardwarethat is not a visible cost at these resolutions, and it removes the interop entirely.
Establish it empirically before shipping: the same stream under
hwdec=nvdec(expect corruption),nvdec-copy(expect clean),no(expect clean, baseline), andauto-copy-safe(expect clean andhwdec-current=nvdec-copy). The log line atMpvWidget.cpp:503already printshwdec-current, so theevidence is one grep per run.
Also check what
auto-copy-saferesolves to on a machine without NVIDIA — the fix must not regressD3D11VA-copy or Android's mediacodec-copy, both of which
HwDecode.hreasons about explicitly.Immediate workaround for a user
Settings → Video → hardware decoding → Off (
video/hwdec=off). Guaranteed-correct software decode,higher CPU. Flipping it and watching the corruption stop is also the cheapest confirmation of this
diagnosis.