# vidcapture
[](https://crates.io/crates/vidcapture)
[](LICENSE)
[](#requirements)
Record your screen and audio from the terminal. Stop it with one key. Cut a
precise range out of the result, and caption it, without opening an editor.
```
$ vidcapture start
Capturing [12s elapsed], press s to stop.
Saved to vidcapture_2026-08-27_21-40-03.mp4
$ vidcapture cut vidcapture_2026-08-27_21-40-03.mp4 --from 3s --to 9s
Cut saved to vidcapture_2026-08-27_21-40-03_cut.mp4
$ vidcapture label talk.mp4 -l "text=Setting up,from=1m32s,to=2m"
Labeled video saved to talk_labeled.mp4
```
No GUI, no project files, no export dialog — one binary that shells out to
`ffmpeg` and gets out of the way.
## Features
- **`start`** — records the full screen plus system audio and microphone,
mixed into one track, as H.264/AAC MP4.
- **Stop on demand or on a timer** — press `s` to stop, or set `-d 30s` /
`-d 2m` to stop automatically.
- **Interval mode** (`-e 10s`) — splits a long recording into seamless,
independently playable segments as it goes, so a crash only costs the
current segment.
- **`cut`** — pulls a millisecond-precise range out of any existing video
into a new file. The source is opened read-only and never modified.
Re-encodes by default for a frame-accurate start; `--fast` stream-copies
for a near-instant, keyframe-aligned cut.
- **`label`** — draws timed text onto any existing video: each label appears
for the span you give it and disappears again. Repeat `-l` for as many
labels as you need. A label needs only its text and its window — it comes
out white on a translucent band, readable over whatever is behind it — and
takes a position (top or bottom), color, size, and band color when the
default doesn't fit. The source is never modified.
- **One timespec format everywhere** — `10s`, `1500ms`, `1h30m10s`, or
`00:01:30.500`, accepted by every time-valued flag and label spec key.
- **No lingering partial files** — a failed capture or cut cleans up after
itself.
## Requirements
- macOS (uses `ScreenCaptureKit` via `ffmpeg`'s `avfoundation` input; not
portable to Linux/Windows).
- [ffmpeg](https://ffmpeg.org): `brew install ffmpeg` for `start` and `cut`.
`label` needs the `drawtext` filter: run `brew install ffmpeg-full`, then
`export PATH="$(brew --prefix ffmpeg-full)/bin:$PATH"` so vidcapture uses
that build. Homebrew keeps `ffmpeg-full` outside the default PATH.
- [BlackHole 2ch](https://github.com/ExistentialAudio/BlackHole), only for
`start` (system audio capture): `brew install blackhole-2ch`, then a
one-time Multi-Output Device setup — run `vidcapture help` for the exact
steps. **`cut` and `label` need neither BlackHole nor screen-recording
permission.**
## Install
```
cargo install vidcapture
```
Or build from source:
```
git clone https://github.com/elvisbrevi/vidcapture
cd vidcapture
cargo install --path .
```
Re-running either command upgrades an existing install in place.
## Usage
```
vidcapture start # record until you press 's'
vidcapture start -d 30s # stop automatically after 30 seconds
vidcapture start -e 10s # split into 10-second segments
vidcapture start -o ./recordings/ # save into ./recordings/
vidcapture cut talk.mp4 --length 5s # first 5 seconds
vidcapture cut talk.mp4 --from 10s --to 25s # 10s through 25s
vidcapture cut talk.mp4 --from 1m --length 1500ms --fast # instant, no re-encode
# One label across the bottom, from 1m32s to 2m:
vidcapture label talk.mp4 -l "text=Setting up,from=1m32s,to=2m"
# Several labels in one pass, restyled where the default doesn't fit:
vidcapture label talk.mp4 \
-l "text=Intro,from=0s,to=1m32s,position=top" \
-l "text=Setting up,from=1m32s,to=2m" \
-l "text=Live demo,from=2m,length=90s,color=#ffcc00,size=48,background=black@0.7"
```
### Label specs
Each `-l` takes one label as comma-separated `key=value` pairs:
| `text` | The text to draw. **Required.** | — |
| `from` | When the label appears. | `0s` |
| `to` | When it disappears. Use this *or* `length`. | — |
| `length` | How long it stays up. Use this *or* `to`. | — |
| `position` | `top` or `bottom`. | `bottom` |
| `color` | Text color. | `white` |
| `size` | Font size in pixels. | `32` |
| `background` | Color of the band behind the text, or `none`. | `black@0.5` |
Only `text` and one end of the window are required. Everything else has a
default chosen to be readable over footage you haven't seen: white text at
32px on a translucent black band. Set `background=none` if you'd rather the
text sat directly on the video.
Times take any timespec (`92s`, `1m32s`, `00:01:32`). Colors are ffmpeg
names or `#RRGGBB`, with an optional alpha suffix: `white`, `#ffcc00`,
`black@0.5`. To put a literal comma in a label's text, write `\,`.
The labeled video is written beside the source as `talk_labeled.mp4` unless
`-o` says otherwise; `talk.mp4` itself is never modified. Labels are drawn
into the pixels, so re-labeling means going back to the source.
Every flag, the full timespec grammar, and BlackHole setup instructions are
in `vidcapture help`.
## Agent skill
This repo ships a portable `vidcapture` development skill at
`.agents/skills/vidcapture/SKILL.md`. [Codex](https://openai.com/codex/)
and [OpenCode](https://opencode.ai/) discover it from `.agents/skills/`;
[Claude Code](https://claude.com/claude-code) uses the compatibility path
`.claude/skills/vidcapture/`, which points to the same source.
A release build (`cargo install`, `cargo build --release`) installs the skill
to `~/.agents/skills/vidcapture/` and `~/.claude/skills/vidcapture/`. Debug
builds do not install it. Set `VIDCAPTURE_SKIP_SKILL_INSTALL=1` to skip the
installation. See `build.rs`.
## Design docs
- [`PRD.md`](PRD.md) — product spec and implementation decisions
- [`CONTEXT.md`](CONTEXT.md) — domain vocabulary
- [`docs/adr/`](docs/adr) — architecture decision records
## License
[MIT](LICENSE)