burns.backends#

Pluggable single-clip render backends — the open-closed seam.

A backend turns one (image, BurnsPath, duration) into one video file. They all share the RenderBackend call signature and live in a name-keyed RENDER_BACKENDS registry, so adding a backend (an FFmpeg zoompan fast-path, a future GPU path) never edits the facade — you register_backend() it and select it with backend="…".

The default backend, "pillow", drives a lazy moviepy.VideoClip whose per-frame closure calls burns._frame.sample_frame(), so the whole clip is never materialized in memory and the spec-to-pixel mapping stays in one place. The multi-panel film renderer in burns.render deliberately does not go through this registry — it is a single-pass encode across panels, which a per-clip backend cannot express.

Module Attributes

DFLT_BACKEND

Still pillow, deliberately.

Functions

get_backend([name])

Look up a registered backend by name.

pillow_backend(img_np, img_w, img_h, path, ...)

Default backend: lazy moviepy VideoClip + per-frame Pillow resampling.

register_backend(name, backend)

Register a render backend under name (open-closed extension point).

Classes

RenderBackend(*args, **kwargs)

Encode one image + BurnsPath into a video file at output.

burns.backends.DFLT_BACKEND = 'pillow'#

Still pillow, deliberately. The two backends are NOT pixel-equivalent (measured ~47 dB, dominated by resampler choice rather than by anything either does wrong), so flipping the default would silently change the output of every existing caller. The speed advantage that motivates the ffmpeg path is also still unmeasured — see thorwhalen/burns#12.

class burns.backends.RenderBackend(*args, **kwargs)[source]#

Bases: Protocol

Encode one image + BurnsPath into a video file at output.

Args mirror burns.ken_burns_video() after argument resolution: the image is already a decoded (H, W, C) uint8 array with its pixel size, and the output frame size (out_w, out_h) is already even-snapped. Returns the written path.

burns.backends.get_backend(name='pillow')[source]#

Look up a registered backend by name.

Return type:

Callable[..., Path]

Examples

>>> get_backend() is get_backend("pillow")
True
burns.backends.pillow_backend(img_np, img_w, img_h, path, *, duration, fps, output, out_w, out_h, codec, audio_codec, **write_kwargs)[source]#

Default backend: lazy moviepy VideoClip + per-frame Pillow resampling.

Quality default; jitter-free (the window is computed in floating point and rasterized once per frame, so there is no FFmpeg zoompan integer-rounding stair-step).

Return type:

Path

burns.backends.register_backend(name, backend)[source]#

Register a render backend under name (open-closed extension point).

Return type:

None