junkpile

15 — wgpu Native Video Decoder

A standalone Tauri 2 + Rust + wgpu example that decodes local video files through FFmpeg, publishes only the newest decoded BGRA frame, and uploads that frame to a native Metal / Vulkan / DX12 texture.

video file
   ↓
ffprobe metadata inspection
   ↓
FFmpeg decoding process
   ↓
packed BGRA frames over stdout
   ↓
latest-frame boundary in Rust
   ↓
Bgra8UnormSrgb wgpu texture
   ↓
WGSL processing
   ↓
native GPU surface

Prerequisites

The project requires both ffmpeg and ffprobe on PATH.

macOS with Homebrew:

brew install ffmpeg

Confirm both commands are visible:

ffmpeg -version
ffprobe -version

Run

npm install
npm run dev:metal

Or allow wgpu to choose the backend:

npm run dev

Included transport controls

Included GPU processing

Why an FFmpeg process?

This example deliberately tests a stable native media boundary without making the Rust crate depend on a particular FFmpeg development-library installation. Rust owns the process, frame pacing, latest-frame publication, transport commands, diagnostics, and GPU upload. FFmpeg owns codec demuxing and decoding.

A production application could bundle FFmpeg as a Tauri sidecar or replace the process boundary with platform APIs such as VideoToolbox, Media Foundation, or GStreamer. This project keeps that decoder boundary explicit so future developers can replace it without rewriting the renderer.

Current scope

RGB separation

The RGB separation control is measured in source pixels, not normalized UV units. This keeps a value such as 24 px visually consistent across source resolutions. Red is sampled in one direction, green from the original coordinate, and blue in the opposite direction.

Effect-aware controls

The controls window now exposes only parameters used by the selected Processing mode. Global image controls remain visible at all times. For example, selecting RGB separation reveals both Effect strength and RGB separation (source px); selecting Clean source hides effect-only parameters so they cannot appear broken while inactive.