mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 22:37:28 +00:00
Video calls (PairUX→ASCII), agent@ chat, and finger routes
ssh video-<code>@ joins a PairUX/LiveKit call rendered as truecolor ASCII (▀ half-blocks, 2 pixels per cell); video@ prompts for a code. Codes are minted by PairUX only. Pipeline: VP8 RTP → PLI keyframe requests → ivfwriter remux → ffmpeg decode/scale → RGB24 → ANSI → bubbletea over the SSH PTY. Subscriber-only, no audio in v1. ssh agent@ opens a persisted chat with the operator's agent — AGENTBBS_AGENT_CMD runs per message (stdin→stdout), e.g. `claude -p`. ssh <member>@ with someone else's name prints a classic finger card (.plan, member since, last seen) and disconnects; your own name still enters the hub. cmd/lkpublish: dev publisher for testing (explicit -fps pacing; lksdk IVF replay mispaces from file timebase alone, measured 1fps from a 15fps file; dimensions required or dynacast pauses the track). Verified end-to-end against livekit-server --dev: 128k truecolor cells / 20k distinct colors streamed over a real SSH session; chat round-trip and finger card verified over SSH. Go toolchain pinned to 1.26 via mise.toml (lksdk requirement). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f3b085a08f
commit
9b0f465946
14 changed files with 1397 additions and 46 deletions
52
internal/ascii/ascii.go
Normal file
52
internal/ascii/ascii.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Package ascii converts raw video frames into truecolor terminal art.
|
||||
//
|
||||
// Each character cell renders two vertical pixels using the upper-half block
|
||||
// (▀): foreground colors the top pixel, background the bottom — the same
|
||||
// technique doom-ascii uses. A WxH terminal therefore displays a Wx(2H)
|
||||
// pixel image.
|
||||
package ascii
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FrameRGB renders a packed RGB24 frame (w*h*3 bytes, as ffmpeg's rawvideo
|
||||
// rgb24 emits) sized exactly for the target cell grid: w columns, h*2 rows
|
||||
// of pixels. Rows are joined with \r\n so the output is PTY-safe.
|
||||
func FrameRGB(buf []byte, w, h int) string {
|
||||
if len(buf) < w*h*3 || w <= 0 || h <= 0 {
|
||||
return ""
|
||||
}
|
||||
rows := h / 2
|
||||
var b strings.Builder
|
||||
b.Grow(rows * w * 40)
|
||||
for row := 0; row < rows; row++ {
|
||||
top := row * 2
|
||||
bot := top + 1
|
||||
for x := 0; x < w; x++ {
|
||||
ti := (top*w + x) * 3
|
||||
bi := (bot*w + x) * 3
|
||||
fmt.Fprintf(&b, "\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dm▀",
|
||||
buf[ti], buf[ti+1], buf[ti+2],
|
||||
buf[bi], buf[bi+1], buf[bi+2])
|
||||
}
|
||||
b.WriteString("\x1b[0m")
|
||||
if row != rows-1 {
|
||||
b.WriteString("\r\n")
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// FitEven clamps a terminal geometry to an even pixel height for the
|
||||
// half-block renderer and returns pixel dimensions (pw, ph) for the decoder.
|
||||
func FitEven(cols, rows int) (pw, ph int) {
|
||||
if cols < 8 {
|
||||
cols = 8
|
||||
}
|
||||
if rows < 4 {
|
||||
rows = 4
|
||||
}
|
||||
return cols, (rows - 1) * 2 // leave one status line
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue