Skip to content

vf_amf: add amf_vqe filter (AMD Video Quality Enhancer) - #18281

Open
JuliusBairaktaris wants to merge 3 commits into
mpv-player:masterfrom
JuliusBairaktaris:amf-vqe
Open

vf_amf: add amf_vqe filter (AMD Video Quality Enhancer)#18281
JuliusBairaktaris wants to merge 3 commits into
mpv-player:masterfrom
JuliusBairaktaris:amf-vqe

Conversation

@JuliusBairaktaris

Copy link
Copy Markdown

Adds --vf=amf_vqe, a filter based on the AMF Video Quality Enhancer
component, which reduces the blocking artifacts introduced by AVC/HEVC
compression at low bit rates while preserving details. This is a good fit for
the d3d11va playback path on AMD hardware, where low bitrate streams are
common.

The filter is implemented in video/filter/vf_amf.c alongside amf_frc,
since it needs exactly the same scaffolding: AMF runtime loading, a separate
D3D11 context, shared-texture conversion in both directions, and the
submit/query loop. It is a plain one frame in, one frame out component, so no
changes to process() were needed.

The first commit is a mechanical rename (frc -> comp, init_frc ->
init_component, generic log messages) to make the shared code component
agnostic; no functional change. The second adds the filter itself.

Implementation notes:

  • Init() is deferred to the first frame and called with the actual D3D11
    texture dimensions, following the existing FRC precedent.
  • AMF_VIDEO_ENHANCER_OUTPUT_SIZE is documented as required for the
    enhancement to be performed, but is not exposed by all runtime versions
    (AMF_NOT_FOUND on the driver tested below), where it is not needed
    either. Setting it is therefore best effort.
  • The engine type is pinned to AMF_MEMORY_DX11 like amf_frc does.
  • Only attenuation is exposed. AMF_VE_FCR_RADIUS is deprecated upstream
    and documented as having no effect, and AMF_VE_FCR_SPLIT_VIEW is
    documented as experimental (it renders a squeezed side by side comparison),
    so neither seemed worth exposing.

Tested on Windows 11 with an RX 9070 XT (RDNA4), driver 32.0.31021.5001,
using --hwdec=d3d11va --d3d11va-zero-copy=yes --gpu-api=d3d11 --vo=gpu-next on low bitrate 720p H.264:

  • The filter initializes on d3d11va frames and plays back cleanly.
  • Comparing the same frame with and without the filter gives PSNR 42.5 dB /
    SSIM 0.970 at attenuation=0.4, and PSNR 51.3 dB at the minimum
    attenuation=0.02, i.e. the option is applied and behaves monotonically.
  • amf_frc still works after the rename commit.

Note on the lavfi alternative: FFmpeg recently gained a vqe_amf filter, but
it currently fails to initialize with d3d11va input
(https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23874), and even with that fixed
it returns AMF surfaces on a link negotiated as D3D11
(https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/23875), so it is not usable
from mpv's filter chain. A native filter also avoids the bridge entirely,
which is the same reasoning that applies to amf_frc.

Copilot AI review requested due to automatic review settings July 23, 2026 01:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread video/filter/vf_amf.c Outdated
Comment thread video/filter/vf_amf.c Outdated
Comment thread video/filter/vf_amf.c Outdated
Comment thread video/filter/vf_amf.c Outdated
Comment thread video/filter/vf_amf.c Outdated
Comment thread video/filter/vf_amf.c Outdated
Comment thread video/filter/vf_amf.c Outdated
Comment thread video/filter/vf_amf.c Outdated
Split the shared AMF runtime, D3D11 context and surface conversion out of the
FRC-specific code so more filter components can reuse it. The AMF component is
now created by name, the submit/query helper takes a per-filter init callback,
and the filter's options, properties and process loop stay in their own section.

The amf_frc processing is otherwise unchanged, except that create_amf_context()
no longer calls destroy() on its error path: the single talloc_free() in the
create helper already runs the filter destructor, so dropping the extra call
fixes a pre-existing double-free of the amf_priv when context init fails.
Add a filter based on the AMF Video Quality Enhancer component. It improves the
perceptual quality of low bitrate video by reducing blocking and mosquito noise
artifacts. Unlike amf_frc it is a plain one frame in, one frame out component,
so it reuses the shared scaffolding but has its own process loop that keeps the
frame rate unchanged.
@JuliusBairaktaris

Copy link
Copy Markdown
Author

Thanks for the review — reworked it along the lines you suggested:

  • amf_frc and amf_vqe are now distinct filters (own options struct, init and process), sharing only the generic AMF scaffolding — runtime/context/component creation and surface conversion. The kind enum and the branching on it are gone.
  • amf_vqe gets its own one-in-one-out loop: no has_frame, and a NULL output surface is treated as "needs more input" rather than an error, so it can't alter the frame rate. You were right that the FRC loop wasn't valid here.
  • Smaller points: VQE OUTPUT_SIZE moved in with the other VQE properties, kept the goto err cleanup, and made the D3D11-input message specific (and dropped the duplicate).

Split into two commits: the generic split (no functional change to amf_frc), then the new filter. The split commit also drops a redundant destroy() in create_amf_context() that double-freed amf_priv on a context-init failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants