PNGGIF::VideoSource
Decodes a video file into animation frames using external ffmpeg (with
ffprobe for dimensions/frame rate), so a consumer (e.g. Crysterm's
Widget::Media backends) can play it like an animated GIF. ffmpeg scales
frames to a small cap and emits raw RGBA on stdout; each frame is w*h*4
bytes, converted into a PNGGIF::Bitmap.
Two decoders, chosen per #mode's decode strategy (auto by default):
- Eager (
#decode) — reads every frame up front into a frame-backedPNGGIF::PNG(capped by max_frames). Best for short, looping clips: decode once, loop from RAM for free, instant resize re-sample. - Streaming (
Stream) — a live ffmpeg pipe yielding frames on demand, constant memory regardless of length, fast first paint, re-decodes on loop. Best for long videos that can't fit in memory.
auto streams when the estimated frame count exceeds max_frames, else
eager. Nothing here raises: failures yield nil and the caller falls
back to its usual "could not load" state.
The sizing/rate/strategy knobs are plain parameters with the DEFAULT_*
constants below as defaults; in Crysterm they were read from
Crysterm::Config, and its adapter reinstates those config values as the
parameter defaults.
Constants
Default cap on the sampled frame rate (the max_fps parameter of
#decode/Stream.open). Mirrors the former video.fps crysterm
config default.
Default safety cap on frames decoded eagerly into memory, and the
Decode::Auto stream-vs-eager threshold (the max_frames parameter of
#decode/#mode). Mirrors the former video.max_frames crysterm
config default; longer videos are truncated to this many frames.
Default long-edge pixel size frames are scaled to (the cap parameter
of #decode/Stream.open). Mirrors the former video.max_size
crysterm config default: terminal boxes are small; smaller = faster,
less memory.
File extensions routed through ffmpeg as video. .ogg is excluded
(commonly audio-only); use .ogv for Ogg video.
Instance methods
Allocates a w×h fully-transparent bitmap for in-place reuse by the
Stream ping-pong. (PNGGIF::Pixel is a value struct, so Array.new
fills the row with independent copies — no shared reference.)
:nodoc:
Caps sw×sh to a cap long edge (preserving aspect), forcing even dimensions (yuv420-based codecs require even width/height). :nodoc:
Decodes file eagerly into a frame-backed PNGGIF::PNG, or nil on any
failure. cap is the long-edge pixel size frames are scaled to;
max_fps caps the sampled frame rate; max_frames caps how many frames
are read into memory.
Deep-copies a bitmap (new outer + inner arrays) so a caller can retain a snapshot that won't be mutated when a ping-pong buffer is later reused. :nodoc:
Estimates the total frame count via ffprobe (nb_frames, else
duration × fps), or nil when unknown.
ffmpeg argv that decodes file to raw RGBA frames of w×h at fps. :nodoc:
Runs ffprobe for the first video stream, requesting entries (a
comma-separated stream=… field list), and returns the printed
key => value pairs — or nil if ffprobe is missing / fails.
:nodoc:
Overwrites bmp in place from the raw RGBA frame in buf, reusing the existing row/outer arrays (only the value-struct pixels are reassigned). bmp must already be sized w×h. :nodoc:
Resolves the decoder strategy for file from decode (auto | eager
| stream). auto streams when the estimated frame count exceeds
max_frames (an unknown length stays eager — the cap then protects
memory by truncation).
Parses an ffprobe avg_frame_rate value ("num/den") into fps, or 0.0
when unset/unusable.
:nodoc:
Reads width / height / average frame rate via ffprobe. Returns nil if
ffprobe is missing or the file has no usable video stream.
:nodoc:
Reads exactly buf.size bytes into buf, returning false at EOF (a
short trailing read — e.g. a truncated final frame — is discarded).
:nodoc:
Probes file and resolves the capped frame dimensions and sampled fps as
{w, h, fps}, or nil when ffprobe can't read it. Shared setup for both
eager #decode and streaming Stream.open.
:nodoc:
The sampled fps: the source rate capped to max_fps (never below 1). :nodoc:
Converts one raw RGBA frame buffer into a PNGGIF::Bitmap.
:nodoc: