NURLNURL registrynurl-lang.org →

← video

video 0.2.1 API

main.nu

packages/video/src/main.nu — the video CLI.

video frames <file> [--fps n] [--out dir] [--quiet] video probe <file.avi>

frames extracts JPEG frames from a video — MJPEG AVI in pure NURL, anything else through ffmpeg when it is installed. probe opens an AVI and reports what the extractor would see: the frame rate, the video stream, where the movi payload sits.

API

@ main → i


video.nu

packages/video/src/video.nu — a video file, as frames on disk.

( vid_extract path fps outdir verbose ) → !i String frames kept

A vision tool wants ordered frames; a video is the natural way to shoot them. This extracts JPEG frames, numbered 000000.jpg on, into a directory of the caller's choosing — vid_frames_dir names the <video>_frames/ convention next to the file — and a directory pipeline takes it from there.

Two extraction paths:

MJPEG in AVI — parsed HERE, in pure NURL. An AVI is a RIFF tree and an MJPEG frame chunk is a complete JPEG, which packages/image already decodes; extraction is finding the '..dc' chunks of the video stream and writing their bytes to files. Cameras, OBS and ffmpeg can all record MJPEG.

Everything else (H.264 in MP4, HEVC, VP9, ...) — delegated to ffmpeg when it is on PATH. A from-scratch H.264 decoder is not this package's fight. When ffmpeg is absent the error says exactly what to install or how to record instead.

The sampling stride is computed from the stream's own frame rate: keep every round(src_fps / target_fps)-th frame, floor 1.

( vid_is_video path ) → b the extension is a video's ( vid_frames_dir path ) → String <dir>/<stem>frames ( vidavi_open path ) → !VidAvi String ( vid_avi_fps_num v ) ( vid_avi_fps_den v ) ( vid_avi_vstream v ) ( vid_avi_movi_off v ) ( vid_avi_movi_end v ) → i stream metadata ( vid_avi_extract v outdir stride ) → !i String ( vid_avi_close v ) → v early release (optional) ( vid_extract path fps outdir verbose ) → !i String

A VidAvi is a handle: every copy is the same open file, and the last owner closes it. Nothing here is released by hand.

API

@ vid_is_video s path → b

A positional argument with one of these extensions is a video, the way a directory is a frames directory.

@ vid_frames_dir s path → String

<dir>/<stem>_frames — the conventional place: next to the video, named after it, so what a run leaves behind is self-explaining.

: VidAviImpl

: VidAviImpl {
    File f  // closed by its last owner (the drop glue), as vid_avi_close did
    i fsize
    i fps_num  // dwRate of the vids stream
    i fps_den  // dwScale
    i vstream  // index of the vids stream, for the 'NNdc' fourcc
    i movi_off  // where the movi LIST's payload starts
    i movi_end
}

: VidAvi { s ctl }

An opened AVI is a handle: every copy is the same open file, and the last owner closes it. vid_avi_close is an optional early release.

@ VidAvi_drop sink VidAvi h → v

@ vid_avi_close sink VidAvi v → v

Let go of v now rather than at the end of its owner's scope; the last owner's drop closes the file.

@ vid_extract s path i fps s outdir i verbose → !i String

Extract path at ~fps frames per second into its _frames dir. Returns the directory; the caller feeds it to the ordinary frames pipeline. verbose gates the progress line.