Local video and streams in Emacs as sharp image frames with sound, on ETAF's etaf-video component
app
  • Emacs Lisp 78.7%
  • Python 20.6%
  • Makefile 0.6%
Find a file
Kinneyzhang d5acafe989
Some checks failed
Repository structure / structure (push) Has been cancelled
chore(release): version 0.2.1
Ship the current sources as an immutable archive version.

Validation: `make -C etaf-video-player check` before this header-only change; the
archive install gate re-installs every target afterwards.
2026-10-22 21:39:31 +08:00
.githooks feat: play video with sharp image frames and sound 2026-10-04 09:58:15 +08:00
.github/workflows chore(scripts): sync shared workspace tooling 2026-10-19 12:49:33 +08:00
benchmarks feat: measure the three-pane shell and fill the window edges 2026-10-04 23:35:33 +08:00
docs feat(video-player): play one source as image, ascii or color 2026-10-20 21:21:39 +08:00
examples fix: keep the shell edges continuous and unclutter the header 2026-10-14 00:21:32 +08:00
lisp fix(player): name the render-mode key in the footer hint 2026-10-20 22:22:01 +08:00
scripts chore(scripts): sync shared workspace tooling 2026-10-22 13:16:10 +08:00
tests feat(video-player): play one source as image, ascii or color 2026-10-20 21:21:39 +08:00
.editorconfig feat: play video with sharp image frames and sound 2026-10-04 09:58:15 +08:00
.gitignore feat: play video with sharp image frames and sound 2026-10-04 09:58:15 +08:00
CHANGELOG.md feat(video-player): play one source as image, ascii or color 2026-10-20 21:21:39 +08:00
etaf-video-player.el chore(release): version 0.2.1 2026-10-22 21:39:31 +08:00
LICENSE chore: add the GPL-3.0 license 2026-10-13 00:49:01 +08:00
Makefile feat: rebuild the shell and add subtitles, resume and screenshots 2026-10-13 22:20:56 +08:00
README.md feat(video-player): play one source as image, ascii or color 2026-10-20 21:21:39 +08:00

ETAF Video Player

ETAF Video Player plays a local video or HTTP(S) stream inside an Emacs buffer as sharp image frames with sound. It consumes ETAF's native etaf-video Component: ffmpeg decodes at the display's device pixel ratio so Retina screens stay sharp, an ffplay sidecar plays the source's audio, and every frame is published through ETAF's retained content boundary, so one displayed frame is one incremental publication of a fixed canvas Host, not a full re-render. This application owns the buffer, the canvas size, the render mode, the status line and the keys. Besides image frames it plays the same source as density-ramp characters or true-color half blocks, so a terminal session and a graphical one use one player.

The player at a wide frame:

The player at a wide frame

The same layout in the light appearance:

The player in the light appearance

Capabilities

  • Playback of local video files through ffmpeg; the built-in demo generates a test pattern with ffmpeg's lavfi input, so no file is needed.
  • HTTP(S) video URLs play as streams: ffmpeg decodes them directly, nothing is downloaded first, and a dropped connection is retried for five seconds.
  • Retina-sharp image frames: the producer decodes at the display's device pixel ratio (2x on Retina) and the image is scaled back down for display.
  • Three render modes: image (default, a scaled picture per frame), ascii (one density-ramp character per pixel) and color (two full-color pixels per half-block cell, capped at etaf-video-player-color-columns columns so the per-frame face properties stay affordable). C switches a running player without losing its position, rate or audio state, and etaf-video-player-render-mode picks the mode for new players.
  • Sound: an ffplay sidecar plays the source's audio track at a selectable volume, and pausing releases it so a long pause never catches up.
  • A canvas that fits the window (and follows resizes), four grid presets, and explicit etaf-video-player-set-grid for scripts.
  • A playlist sidebar: open several files (or a URL) as one queue, click an item to play it, and move with next/previous. Loop (off/list/one) and shuffle are one key away, and a finished item advances automatically.
  • Play, pause, single frame stepping, seeking, 0.5x to 8x rates, volume keys and a sound toggle.
  • Subtitles: a SubRip (.srt') or WebVTT (.vtt') track beside a local file loads automatically and renders as one line under the canvas; c hides it, shows it again, or asks for a file, and inline markup is stripped.
  • Resume: local playback positions persist in a small SQLite database and reopening a file continues where it stopped, unless the record is near the start or the end.
  • Screenshots: S decodes the current frame with ffmpeg into a PNG beside the video (or the temporary directory for a stream).
  • Dark and light appearances share one three-pane layout: the page, panes, header and transport each paint one depth step, and the stage follows the appearance so both palettes stay readable. Narrow windows hide the side panes without changing the control identities.
  • A 24fps presentation cap by default, with automatic Retina resolution; etaf-video-player-display-fps changes the cap for new players.
  • Latest-frame-wins display: when decoding outruns display, older frames are dropped instead of replayed as a backlog.
  • etaf-video-player-state exposes the live player as plain data for scripts and tests.
  • Without a graphical frame the Component falls back to a density-ramp canvas, so the same command still shows the video in a terminal; asking for ascii or color explicitly gives the same characters in any frame.

Prerequisites

Requirement Why Check
Emacs 29.1 or newer the ETAF runtime emacs --version
etaf, etaf-ui, ebox, ecss, etaf-db the framework and the playback history database; each root entry must be on load-path every checkout is readable
ffmpeg decoding and scaling ffmpeg -version
ffprobe duration, frame rate and video size ffprobe -version
ffplay sound ffplay -version

ffprobe is optional: without it playback uses the source defaults for dimensions and rate and the status line shows no total duration. ffplay is only needed for sound; without it playback stays silent. The decoder programs, the density ramp and the display rate are the etaf-video-* variables documented in ETAF's manual.

Installation

(add-to-list 'load-path "/path/to/ecss")
(add-to-list 'load-path "/path/to/ebox")
(add-to-list 'load-path "/path/to/etaf")
(add-to-list 'load-path "/path/to/etaf-ui")
(add-to-list 'load-path "/path/to/etaf-db")
(add-to-list 'load-path "/path/to/etaf-video-player")
(require 'etaf-video-player)

(global-set-key (kbd "C-c v") #'etaf-video-player-open-file)

Minimal example

(etaf-video-player-demo)                 ; generated test pattern, no file
(etaf-video-player-open-file "clip.mp4") ; a local video with sound
(etaf-video-player-open-url "https://example.com/clip.mp4") ; a network stream
(etaf-video-player-open '(:file "clip.mp4" :columns 120 :rows 34 :volume 60))
(etaf-video-player-open '(:file "clip.mp4" :mode 'ascii :columns 96))
(etaf-video-player-set-render-mode 'color)       ; true-color half blocks
(etaf-video-player-cycle-render-mode)            ; image, ascii, color
(etaf-video-player-open '(:playlist ((:file "a.mp4") (:file "b.mp4")) :loop 'all))
(etaf-video-player-set-audio nil)        ; watch it muted
(etaf-video-player-subtitle-load "clip.srt")    ; attach a subtitle track
(etaf-video-player-save-screenshot "frame.png") ; save the current frame
(etaf-video-player-history-position "clip.mp4") ; the saved playback record

Keys

Key Action
SPC play or pause
n pause and present one more frame
f / b seek five seconds forward or backward
+ / - raise or lower the playback rate
[ / ] lower or raise the volume by 10
a toggle sound
1 .. 4 grid presets (64, 96, 128, 176 columns, rows follow the aspect)
F fit the canvas to the window
o / u / d open a file / open a URL / replay the built-in demo
> / < next / previous playlist item
c hide, show or load subtitles
S save the current frame as a PNG
L / s cycle loop mode / toggle shuffle
T switch dark/light appearance
? / C-c h searchable help inside the player
q close the player

The header's help button opens the same read-only, themed modal. / searches the effective bindings; Escape leaves search editing before closing help. Clicking the dimmed background closes it and restores the player focus.

Checks

make check    # structure + byte compilation (warnings as errors) + acceptance + API boundary
make test     # the public acceptance scenarios only

The acceptance suite drives the repository's fake ffmpeg, ffprobe and ffplay fixtures, so it needs no real decoder, no network, no sound and no frame; one scenario runs the real ffmpeg when one is installed.

Documentation

  • The manual: playback, image frames, sound, scripting, variables and troubleshooting.
  • Architecture: module responsibilities, data flow, invariants and the ownership boundary with ETAF.
  • The root entry Commentary: the authoritative list of supported functions and variables.