|
| 1 | +# TAGLINE |
| 2 | + |
| 3 | +Command-line video editor and compositor |
| 4 | + |
| 5 | +# TLDR |
| 6 | + |
| 7 | +**Trim** a clip (copied to a keyframe unless `--exact`) |
| 8 | + |
| 9 | +```geneva trim [match.mp4] -o [goal.mp4] --from [41:10] --to [41:40]``` |
| 10 | + |
| 11 | +**Join** clips with a one-second crossfade |
| 12 | + |
| 13 | +```geneva concat [day1.mp4] [day2.mp4] -o [trip.mp4] --crossfade 1s``` |
| 14 | + |
| 15 | +**Convert** for the web encode table |
| 16 | + |
| 17 | +```geneva convert [talk.mov] -o [talk.mp4] --for web``` |
| 18 | + |
| 19 | +**Burn** subtitles into the picture |
| 20 | + |
| 21 | +```geneva subtitles [talk.mp4] -o [talk-subbed.mp4] --burn [talk.srt]``` |
| 22 | + |
| 23 | +**Render** a JSON timeline (titles and graphics as HTML/CSS) |
| 24 | + |
| 25 | +```geneva render [edit.json] -o [out.mp4]``` |
| 26 | + |
| 27 | +**Probe** a file's streams, duration, and colour tags |
| 28 | + |
| 29 | +```geneva probe [talk.mp4]``` |
| 30 | + |
| 31 | +Write a **still** from a video |
| 32 | + |
| 33 | +```geneva frame [talk.mp4] -o [thumb.jpg] --at 12s --width 640``` |
| 34 | + |
| 35 | +Print the **built-in manual** (for scripts and agents) |
| 36 | + |
| 37 | +```geneva guide``` |
| 38 | + |
| 39 | +# SYNOPSIS |
| 40 | + |
| 41 | +**geneva** [**--format** _human_|_json_] _subcommand_ [_options_] |
| 42 | + |
| 43 | +**geneva** **trim** _input_ **-o** _file_ [**--from** _time_] [**--to** _time_ | **--duration** _time_] [_encode-options_] |
| 44 | + |
| 45 | +**geneva** **concat** _input_... **-o** _file_ [**--crossfade** _time_ | **--fade** _time_] [_encode-options_] |
| 46 | + |
| 47 | +**geneva** **convert** _input_ **-o** _file_ [_size-options_] [_encode-options_] |
| 48 | + |
| 49 | +**geneva** **render** _timeline.json_ **-o** _file_|_dir_ [_encode-options_] |
| 50 | + |
| 51 | +**geneva** **probe** _file_ |
| 52 | + |
| 53 | +# PARAMETERS |
| 54 | + |
| 55 | +**--format** _human_|_json_ |
| 56 | +> Global. **json** prints one JSON document on stdout. In **human** mode diagnostics go to stderr. Default **human** |
| 57 | +
|
| 58 | +Times accept `1.5`, `1.5s`, `1500ms`, `45f` (frames at the source rate), or `00:00:01.5`. |
| 59 | + |
| 60 | +**trim** |
| 61 | +> Cut a range. **--from** starts the keep (default: beginning). **--to** or **--duration** ends it (default: end of the file). Copied by default, with the cut moved back to a keyframe; **--exact** for the exact frame |
| 62 | +
|
| 63 | +**concat** |
| 64 | +> Join two or more inputs back to back. Identical stream parameters are copied; anything else is rendered. **--crossfade** _TIME_ dissolves (constant-power audio). **--fade** _TIME_ dips through **--fade-color** (black default) |
| 65 | +
|
| 66 | +**convert** |
| 67 | +> Re-encode, optionally changing container, codec, size, or rate. **--width** / **--height** (one keeps the aspect, rounded even). **--fps**. **--fit** `contain`|`cover`|`fill`. **--crop** `X,Y,WxH` or centred `WxH` (pixels or percentages). **--speed** _FACTOR_ (`2` twice as fast; pitch follows) |
| 68 | +
|
| 69 | +**resize** |
| 70 | +> **convert** with **--width** or **--height** required (or **--crop**) |
| 71 | +
|
| 72 | +**overlay** _input_ _overlay_ **-o** _file_ |
| 73 | +> Image or video (without its audio) over the input. **--at** `top-right` (default), `top-left`, `bottom-left`, `bottom-right`, `center`. **--margin** (default 24 px). **--scale**. **--opacity** 0–1. **--start** / **--duration** |
| 74 | +
|
| 75 | +**audio** |
| 76 | +> One of **--extract** (audio file; **--speech** writes 16 kHz mono), **--mute**, **--replace** _FILE_, or **--mix** _FILE_ [**--gain** _DB_] |
| 77 | +
|
| 78 | +**subtitles** |
| 79 | +> One of **--add** _FILE_ (repeatable, with **--language** codes in order), **--burn** _FILE_ (`.srt`, `.vtt`, or word-timed `.json`; **--position**, **--style** JSON, **--highlight** _COLOR_, **--fit**), or **--extract** [**--track** _N_] |
| 80 | +
|
| 81 | +**render** _timeline_ **-o** _file_|_dir_ |
| 82 | +> Render a JSON document. The container comes from the extension unless the document sets it. With an `outputs` map, **-o** is a directory |
| 83 | +
|
| 84 | +**frame** _timeline_|_video_ **-o** _picture_ |
| 85 | +> One PNG or JPEG. **--at** _TIME_ or **--frame** _N_. **--width** / **--height**. Default output `frame.png` |
| 86 | +
|
| 87 | +**validate** _timeline_ |
| 88 | +> Parse and check. **--probe** also opens media assets. **--assets** _DIR_ is the asset root (default: the timeline's directory) |
| 89 | +
|
| 90 | +**probe** _file_ |
| 91 | +> Container, duration, streams, sizes, rates, rotation, and colour tags |
| 92 | +
|
| 93 | +**schema** |
| 94 | +> Print the JSON Schema of the current timeline format |
| 95 | +
|
| 96 | +**targets** |
| 97 | +> Print the **--for** table (device and platform encode presets) |
| 98 | +
|
| 99 | +**guide** [_TOPIC_] [**--list**] |
| 100 | +> Built-in manual. No topic: the agent guide. Topics: `agents`, `timeline`, `cli`, `errors`, `color`, `architecture` |
| 101 | +
|
| 102 | +**explain** _CODE_ [**--list**] |
| 103 | +> What a diagnostic code means (any case). **--list** prints every code |
| 104 | +
|
| 105 | +Shared encode flags on the verbs (and several on **render**): |
| 106 | + |
| 107 | +**-o** _FILE_ |
| 108 | +> Output path. The extension selects the container |
| 109 | +
|
| 110 | +**--crf** _N_ |
| 111 | +> Constant quality; lower is better. Useful range about 18–30 for H.264/H.265. Forces a re-encode |
| 112 | +
|
| 113 | +**--preset** _NAME_ |
| 114 | +> Encoder preset, `ultrafast` to `veryslow`. Forces a re-encode |
| 115 | +
|
| 116 | +**--codec** _NAME_ |
| 117 | +> `h264`, `h265`, `vp9`, `av1`, `prores`, `dnxhd`, `png`, `mjpeg`. Default: the container's usual codec |
| 118 | +
|
| 119 | +**--for** _TARGET_ |
| 120 | +> Encode for a destination: `phone`, `tablet`, `desktop`, `tv`, `web`, `youtube`, `instagram`, `tiktok`, `podcast`, `x`, `linkedin`, `email`. **--quality** `best`|`good`|`eco` (default **good**). **--budget** _SIZE_ (for example `25MB`) |
| 121 | +
|
| 122 | +**--exact** |
| 123 | +> Frame-accurate cuts. With H.264 and system x264, a smart cut copies untouched GOPs and re-encodes only around cuts and overlays |
| 124 | +
|
| 125 | +**--renderer** _auto_|_cpu_|_gpu_ |
| 126 | +> Who composites frames. **auto** (default) uses a hardware GPU if one is present, else the CPU. Copied streams are copied either way |
| 127 | +
|
| 128 | +**--show-timeline** |
| 129 | +> Print the compiled JSON document instead of rendering |
| 130 | +
|
| 131 | +**--no-audio** |
| 132 | +> Write no audio track |
| 133 | +
|
| 134 | +Also: **--max-bitrate**, **--audio-codec**, **--audio-bitrate**, **--sample-rate**, **--channels**, **--keep-hdr**, **--chunks** `N`|`auto`, **--fill** `bars`|`blur`, **--profile**, **--tune**, **--keyframe-interval**, **--fixed-keyframes**. |
| 135 | + |
| 136 | +# DESCRIPTION |
| 137 | + |
| 138 | +**geneva** is a command-line video editor. Everyday verbs (`trim`, `concat`, `convert`, and the rest) are compiled into a JSON timeline and rendered through the same path as **geneva render**. Titles and graphics are HTML and CSS, drawn by geneva's own layout engine — no browser, Playwright, or filtergraph. |
| 139 | + |
| 140 | +A document names the format version in `"geneva"` (current **1.1**; a `"1.0"` document is read as it is). Assets are media files or HTML. Layers hold clips; clips can be video, audio, HTML, captions, or colour. Each frame is computed from its timestamp, so a render is deterministic. HDR sources are tone-mapped to SDR unless **--keep-hdr**. |
| 141 | + |
| 142 | +The planner picks the cheapest output mode: **copy** (packets copied), **copy-picture**, **smart** (H.264 bytes copied where the picture is untouched), **direct** (decode to encode without the compositor), or **render** (full composite). Errors are reported before rendering, with a code and a JSON path, for example `error[E200]: unknown asset "crad"`. **geneva explain E302** prints what a code means. |
| 143 | + |
| 144 | +Codecs come from FFmpeg's libraries, bundled in the binary. H.264 uses a hardware encoder when one is allowed, else the system's **x264**, else bundled OpenH264. Smart cut and smaller H.264 files need x264 on the system (`libx264` on Debian/Ubuntu, `brew install x264` on macOS, or `GENEVA_X264` / a `libx264-*.dll` beside `geneva.exe` on Windows). H.265 is hardware-only. |
| 145 | + |
| 146 | +Markup supports flexbox and block layout, absolute positioning, gradients, `box-shadow`, `filter: blur()`, `clip-path: polygon()`, `mix-blend-mode`, `background-clip: text`, web fonts shipped as assets, and CSS `@keyframes` played as written. Not supported: CSS grid, floats, transitions, static `transform` (transforms come from animations), and JavaScript. An unsupported declaration is reported by name and skipped. |
| 147 | + |
| 148 | +`--format json` works on every command. Exit **0** on success, **1** if the timeline is invalid, **2** for a usage error, **3** if rendering or encoding failed. |
| 149 | + |
| 150 | +# CAVEATS |
| 151 | + |
| 152 | +This **geneva** is geneva-render's video editor, not Georgetown's network-evasion toolkit of the same name. |
| 153 | + |
| 154 | +No GUI and no hosted service. One binary for Linux (x64, arm64), macOS (Apple silicon), and Windows (x64). The install script is a curl-to-shell download of a GitHub release. |
| 155 | + |
| 156 | +OpenH264 is bundled because x264 is GPL; without x264, smart cut is off and H.264 files are larger. VideoToolbox and NVENC at constant quality, and bundled OpenH264 always, ignore **--max-bitrate**; the report says so. `--budget` puts VideoToolbox in bitrate mode. |
| 157 | + |
| 158 | +There is no fixed average bitrate, CBR, or two-pass encode: video is constant quality under an optional ceiling. |
| 159 | + |
| 160 | +# HISTORY |
| 161 | + |
| 162 | +Written by Francesco Benetti. MIT licensed. First public release **1.0.0** (2026-09-25); current **1.1.0** (2026-09-28), timeline format **1.1**. |
| 163 | + |
| 164 | +# SEE ALSO |
| 165 | + |
| 166 | +[ffmpeg](/man/ffmpeg)(1), [ffprobe](/man/ffprobe)(1), [melt](/man/melt)(1), [handbrakecli](/man/handbrakecli)(1), [whisper](/man/whisper)(1) |
| 167 | + |
| 168 | +# RESOURCES |
| 169 | + |
| 170 | +```[Source code](https://github.com/geneva-render/geneva)``` |
| 171 | + |
| 172 | +```[Homepage](https://genevarender.com)``` |
| 173 | + |
| 174 | +```[Documentation](https://github.com/geneva-render/geneva/blob/main/docs/cli.md)``` |
| 175 | + |
| 176 | +<!-- verified: 2026-09-30 --> |
0 commit comments