NURLNURL registrynurl-lang.org →

← video

video 0.1.0 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_extract v outdir stride ) → !i String ( vid_avi_close v ) → v ( vid_extract path fps outdir verbose ) → !i String

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.

: VidAvi

: VidAvi {
    File f
    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
}

@ vid_avi_open s path → !VidAvi String

Walk the RIFF tree far enough to know the frame rate, which stream is the video, and where the movi payload lives. Everything else in the file is somebody else's business.

@ vid_avi_close VidAvi v → v

@ vid_avi_extract VidAvi v s outdir i stride → !i String

Extract every stride-th video frame as a JPEG file into outdir, stopping the numbering at what was kept. Returns the kept count.

@ 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.