The egami-video command
egami-video inspects, remuxes, cuts, joins and converts video and audio files from a shell or a build script, with no C# to
write. It does the jobs people use ffprobe and ffmpeg for, with Egami.Video's fully managed code. It is the Egami.Video.Cli
package, a .NET tool that carries Egami.Video's version.
egami-video info in.mkv
egami-video remux in.avi -o out.mkv
egami-video trim in.webm --from 0.5 --to 1.5 -o cut.webm
egami-video frames in.mkv --every 0.5 -o shots/{name}-{frame:0000}.png
egami-video run in.avi resize 80x + sharpen 0.5 -o small.mkv
egami-video audio in.mkv -o sound.flac
The command reference lists every option. ffmpeg command lines explains egami-video ffmpeg and
egami-video ffprobe, and egami-video for AI assistants the mcp server.
Installing
dotnet tool install --global Egami.Video.Cli
With the egami tool installed too, egami video info clip.mkv runs egami-video info clip.mkv: egami runs any
egami-<name> program on the PATH. The tool is free of reflection and builds with the trimming and Native AOT analyzers on.
Commands
| Command | What it does | Like |
|---|---|---|
info |
Container, duration, tracks, codecs, tags, chapters, attachments; --packets, --frames, --headers |
ffprobe |
remux |
Copy tracks into another container | ffmpeg -c copy |
trim |
Cut from the keyframe at or before --from to --to |
ffmpeg -ss -to -c copy |
concat |
Join clips with the same tracks and codecs | the concat demuxer |
segment |
Split every --every, at --at times or at --scenes, at keyframes; --hls and --dash for streaming |
-f segment, -f hls, -f dash |
timestamps |
Shift, set a frame rate, repair timestamps | -itsoffset, -r, setts |
extract |
One track, attachments or cover art to their own files | -map 0:a:0 -c copy, -dump_attachment |
embed |
Add tracks, attachments and cover art; set languages and names | -i a -i b -map 0 -map 1 |
metadata |
Write tags and chapters as JSON, or change them | -f ffmetadata, -metadata |
subs |
Convert, shift and sync SRT, WebVTT and ASS files; subtitle tracks to and from text files | ffmpeg -i in.srt out.vtt, -itsoffset, -c:s mov_text |
frames |
Frames as images; Motion JPEG frames as JPEG without decoding | ffmpeg -i in out-%04d.png |
encode |
Images, an image sequence or Y4M into a clip | ffmpeg -framerate 30 -i %04d.png |
run |
egami's steps on every frame, then encode | -vf scale,crop,pad,transpose |
thumbnail |
A representative frame | -vf thumbnail |
scenes |
Where the scenes start, and a frame of each | select='gt(scene,0.4)' |
analyze |
Black, frozen, silent, cropped, interlaced and duplicate stretches, scenes, signal levels and loudness, in one pass | blackdetect, silencedetect, ebur128 … with -f null - |
waveform |
A picture of the sound's waveform | -lavfi showwavespic |
spectrum |
A spectrogram picture | -lavfi showspectrumpic |
generate |
Test clips and sounds: bars, a pattern, solid colors, gradients, tones, noise, sweeps | -f lavfi -i testsrc2, smptebars, sine |
sheet |
A contact sheet of frames with their times | -vf tile=4x4 |
animate |
Animated GIF, WebP or APNG | ffmpeg -i in.mp4 out.gif |
hash |
Hashes of every frame or packet | -f framemd5 |
compare |
PSNR and SSIM between clips | -lavfi psnr, -lavfi ssim |
verify |
Check FFV1 files for damage without decoding | -err_detect crccheck -f null - |
audio |
Audio to WAV, FLAC, Opus, AIFF, CAF or ALAC; audio again while copying video | -vn -c:a flac, -c:a libopus -b:a, -ar, -ac |
join |
Clips of any size, rate and audio fitted to one canvas and joined, with transitions | concat filter with scale, pad, fps; xfade and acrossfade |
fade |
Fade in from and out to a colour, picture and sound | -vf fade -af afade |
overlay |
One clip over another, with --key for a chroma key |
overlay, chromakey, despill |
pip |
A second clip small in a corner | scale and overlay |
stack |
Clips side by side, above each other or in a grid | hstack, vstack, xstack |
speed |
Faster or slower, the sound's pitch kept | setpts with atempo |
reverse |
Backwards, picture and sound | reverse, areverse |
loop |
A clip several times in a row | loop, aloop |
freeze |
One frame held for a while, or the last one at the end | tpad, freezeframes |
slideshow |
Images one after another, with transitions and pan and zoom | loop=1, zoompan, xfade |
stabilize |
Take the shake out of a clip, in two passes | vidstabdetect, vidstabtransform |
edit |
Render an edit file: clips, transitions, layers and effects | -filter_complex |
formats |
The containers and codecs this build reads and writes | ffmpeg -formats |
Commands that copy packets (remux, trim, concat, segment, timestamps, extract, embed, metadata) never decode, so
they work on any codec, H.264 and AAC included. Commands that decode need a codec Egami.Video decodes: Motion JPEG, VP8, VP9, AV1,
FFV1 and uncompressed video, and PCM, FLAC, Opus and Vorbis audio. encode and run write Motion JPEG, VP8, VP9, AV1, FFV1 or uncompressed video; encode --codec vp8, vp9 or av1 takes
--effort and --bitrate (see Encoding video). --keyframe-interval spaces keyframes for any codec.
--codec ffv1 writes lossless FFV1 version 3 to Matroska or AVI, with the archival defaults: every frame a keyframe, slice CRCs
on and a slice grid picked from the frame size. --slices, --no-slice-crc, --coder range|range-default|golomb and
--context small|large change them. run keeps an FFV1 input in FFV1.
Inspecting
info reads the headers without decoding. --all reads every packet too, for packet and keyframe counts and bit rates;
--headers shows the codec headers of each track's first frame; --packets and --frames list every packet and every decoded
frame.
egami-video info in.mkv --all
egami-video info in.webm --packets --track v
egami-video info in.mkv --json
egami-video info in.mp4 --format ffprobe
--format picks how it writes: text (the default), json (Egami's own schema, docs/schemas/egami-video-info-1.json), csv
for packet and frame lists, xml with the same fields as JSON, or ffprobe: JSON with the field names ffprobe -of json uses,
so scripts written for ffprobe read it unchanged. ffprobe lists Matroska cover art as a video stream; egami-video lists it as an
attachment.
Tracks and times
--track (-t) picks tracks by kind and position, language or number:
| Value | Picks |
|---|---|
v, a, s, d |
Every video, audio, subtitle or data track |
a:1 |
The second audio track (counted from 0) |
s:eng |
Subtitle tracks in English (eng and en both match en-US) |
3 |
Track 3, as info numbers it |
Repeat it, or separate values with commas, for several. --no-audio (-an) and --no-subtitles (-sn) leave those out.
Times are seconds (90.5, 500ms, 2m), [HH:]MM:SS[.mmm] (1:30, 01:02:03.250) or a frame number in presentation order
(#1234, counted from 0).
A stream copy can only start on a keyframe. trim starts at the video keyframe at or before --from, ends exactly at --to,
and says on standard error how far the start moved. segment starts each part at the first keyframe at or after its split point;
segment --scenes starts each part at the keyframe at or before its scene's first frame and says on standard error how far
before, with a warning when that is over a second. trim --smart starts on the exact frame: it encodes again only the frames
from --from up to the next keyframe, matching the stream at its own quantizer, and copies the rest; trim --reencode encodes
the whole cut again. segment --scenes --exact cuts every part that way, so each starts on its scene's first frame.
egami-video trim in.webm --from 0.5 --duration 1 -o cut.webm
egami-video trim in.webm --from 0.5 --duration 1 --smart -o exact.webm
egami-video segment in.mkv --every 1 -o parts/{part:000}.mkv
egami-video segment in.mkv --scenes -o shots/{part:000}.mkv -v
egami-video concat parts/001.mkv parts/002.mkv -o joined.mkv
egami-video timestamps in.mkv --shift 0.5 --track a -o later.mkv
Streaming
remux --fragment and encode --fragment write fragmented MP4: a moov with the setup only, then the media as moof and mdat
pairs, each starting at the first video keyframe after 2 seconds (--fragment=4s for another length, --fragment=keyframe for
every keyframe). Such a file is written as it goes, so it suits uploads, cloud storage and browsers' Media Source Extensions.
segment --hls --dash writes the audio and video tracks as CMAF segments into the folder -o names, cut at the first video
keyframe after --every (6 seconds by default), with master.m3u8 and a playlist a track for HLS and manifest.mpd for DASH;
give one of the two for its playlists only. Subtitle and data tracks are left out.
egami-video remux in.mp4 --fragment -o fragmented.mp4
egami-video segment in.mp4 --hls --dash --every 2 -o stream/
Subtitles
subs works on text subtitles: SRT, WebVTT and ASS (SSA is read and written as ASS). subs convert writes another format, named
by -o's extension or --format; what the target cannot hold, such as ASS colours in WebVTT, is left out and reported once per
kind. A file converted to its own format unchanged comes back byte for byte. --from and --to keep a time range (--rebase
moves it to 0), --sort puts cues in order and --fix-overlaps ends each cue when the next starts.
subs shift --by moves every cue (negative shows them sooner); --scale or --fps 23.976:25 retimes them for a speed change
first. subs sync takes sync points, each a time in the subtitles and where it should be in the video: one moves everything, two
far apart also fix a speed difference. Times are exact, so nothing drifts over a long film.
subs extract writes a media file's text subtitle tracks as files, each in its own format unless --format says otherwise
(mov_text becomes SRT). subs embed adds subtitle files to a media file as tracks and copies its other tracks: Matroska keeps SRT,
WebVTT and ASS as they are, WebM takes WebVTT and MP4 mov_text or WebVTT, converted as needed. Give a language after =, name
tracks with --name 1=Commentary, pick the default with --default 1, and leave out the file's own subtitles with --replace.
Files without a byte order mark that are not UTF-8 are read as Windows-1252, or the code page --encoding names; --strict
fails on a malformed file instead of repairing it, and outputs are UTF-8 (--bom and --crlf for Windows tools).
egami-video subs convert in.srt -o in.vtt
egami-video subs shift in.srt --by -0.5 -o sooner.srt
egami-video subs shift in.srt --fps 23.976:25 -o pal.srt
egami-video subs sync in.srt --map 0:01=0:01.2 --map 0:05=0:05.6 -o synced.srt
egami-video subs embed in.webm --add in.srt=eng -o subtitled.mkv
egami-video subs extract subtitled.mkv -o subs/{name}.{format}
egami-video info in.srt
Subtitle files are inputs to the other commands too: info lists one as a file with one subtitle track, and embed --add movie.srt adds it like a track of another file. trim, concat and timestamps move the subtitle tracks of what they cut.
Frames and pictures
frames writes decoded frames in any format egami writes; {frame} in the output is the frame's number in the video. Motion
JPEG frames written as JPEG are the stored frames, byte for byte, without decoding.
egami-video frames in.mkv --count 4 -o shots/{frame:000}.jpg
egami-video frames in.webm --at 0.5,1.5 -o at-{frame}.png
egami-video thumbnail in.webm -o thumb.jpg --size 320x
egami-video scenes in.mkv --json
egami-video scenes in.mkv --fades --frame middle --series scores.csv -o shots/{scene:000}.jpg
egami-video sheet in.mkv --grid 3x2 --timestamps -o sheet.png
egami-video animate in.webm --rate 10 --size 120x -o clip.gif
scenes finds cuts with the adaptive method by default (--method content or histogram, --threshold); --min-length
(once --min-gap) sets the shortest scene, --fades names scenes that start inside a fade and lists the fades, --flash suppress drops flashes instead of merging them, --frame middle or last picks the frame written for each scene, and
--series writes every frame's score to a CSV file.
encode goes the other way: images in name order, an image sequence (shots/{frame:000}.jpg reads frame 0, 1, 2 until one is
missing), or a Y4M file or stream.
egami-video encode "shots/*.jpg" --rate 2 -o slideshow.mkv
Steps on every frame
run takes the steps and + syntax of egami run (steps for run) and applies them to every frame. The
output keeps the source codec when Egami.Video can encode it, else Motion JPEG; --codec chooses. Audio and subtitles are copied,
and every frame keeps its time.
egami-video run in.avi resize 80x + sharpen 0.5 -o small.mkv
egami-video run in.mkv crop 0 10 160 100 + grayscale + save gray.avi --quality 90
egami-video run in.mkv --crop auto resize 80x -o cropped.mkv
--crop auto reads the video once to find its black bars, as analyze -d crop does, and crops them off before the other
steps.
Analysis
analyze reads a file once and runs the detectors --detect (-d) names, or every one with --all: scenes, black,
freeze, crop, interlace, duplicates, signal, blur, blockiness, silence, levels and loudness. Each keeps the
measure and defaults of the ffmpeg filter it stands for, and its settings take that filter's names: --black-min is
blackdetect's d, --silence-noise -50dB silencedetect's n, --crop-limit cropdetect's limit. --start and --duration
look at part of the file. The report is text, --json or --csv (one row per range or value), and --series file.csv adds
per-frame and per-100 ms values: scene scores, black share, signal levels, momentary and short-term loudness.
egami-video analyze in.mkv --all
egami-video analyze in.mkv -d black,silence --black-min 0.5 --json
egami-video analyze in.mkv -d loudness --series loudness.csv
egami-video analyze in.mkv -d black --ffmpeg-log blackdetect
In JSON each file has start, end and frames, and one entry per detector: scenes (scene, frame, start, end, score, kind)
and fades; lists of {start, end, duration} ranges with a value for black, freeze, duplicates and silence (and
silence.1, silence.2 … per channel); and objects for crop, interlace, signal, levels and loudness. Levels and
loudness that do not exist (silence has no peak) are null. --ffmpeg-log prints the lines ffmpeg's filters log instead,
for scripts that read them (ffmpeg command lines).
waveform and spectrum draw the audio as a picture: --size, --channels mixed|stacked, a linear or dB --scale for the
waveform and a linear or log frequency --scale for the spectrum, colors, and --legend for a time axis and level or
frequency labels. generate makes test files: pattern, bars, solid and gradient pictures, with --tone or --audio
for sound, and sine, silence, noise and sweep sounds.
egami-video waveform in.mkv --legend --scale db -o wave.png
egami-video spectrum in.mkv --scale log --legend --size 1200x400 -o spectrum.png
egami-video generate bars --tone -t 10 bars.mkv
egami-video generate sweep -t 5 sweep.flac
Audio
audio decodes the picked audio track to WAV, Wave64, FLAC, Ogg FLAC, Opus, AIFF or CAF, resampling with --rate, mixing with --channels
and converting with --sample-format. With a video container as output it copies the video unchanged and encodes the audio
again. --codec picks FLAC, PCM, Opus or ALAC (Apple Lossless); .opus and WebM outputs get Opus, WAV, Wave64, AVI, AIFF
and CAF get PCM, .m4a gets ALAC, and the rest FLAC. ALAC also goes in .mov, .mka and .caf. --bitrate sets Opus's bitrate
for all channels together (64k a channel by default); --level sets FLAC's compression (0 to 8) or ALAC's (0 fast, 1 the
default). extract writes ALAC tracks to .m4a and AC-3 and E-AC-3 tracks to raw .ac3 and .eac3 files.
The audio filters (see Audio filters) run on the way: --gain in decibels, --fade-in and
--fade-out over a time from the start and to the end, --tempo (0.25 to 4, the pitch kept), --normalize to ebu, atsc,
streaming, podcast or a number of LUFS (two passes: measure, then one gain and a true-peak limiter only where needed),
--trim-silence, and --af with ffmpeg's -af syntax for volume, afade, atempo, adelay, apad, atrim, asetpts, the EQ
filters (highpass, lowpass, bandpass, bandreject, allpass, equalizer, bass, treble), acompressor, agate, alimiter, loudnorm
and silenceremove; an unknown filter stops with the list. --trim-silence and silenceremove refuse a file with video, since
the sound would no longer line up with it. encode takes the same options for its --audio, and run all but the silence
cut for the audio it copies.
egami-video audio in.mkv -o sound.flac
egami-video audio in.webm --rate 44100 --channels stereo --sample-format s16 -o sound.wav
egami-video audio in.mkv --codec flac -o flac-audio.mkv
egami-video audio sound.flac --bitrate 96k -o sound.opus
egami-video audio in.mp4 --bitrate 64k -o opus-audio.webm
egami-video audio sound.flac -o sound.m4a
egami-video audio sound.wav --sample-format s24 -o sound.aiff
egami-video audio sound.wav --trim-silence --normalize podcast -o talk.flac
egami-video audio in.mkv --af "highpass=f=80,acompressor=threshold=-20dB:ratio=3" --fade-out 2 -o cleaner.mkv
Editing
join, fade, overlay, pip and stack build an edit with Egami.Video's editing library (see
Video editing) and encode it again, so their clips may differ in size, frame rate, pixel format
and audio: every clip is fitted to one canvas (--size, --rate and --fit, from the first clip by default) and its sound
resampled. --transition joins clips with fade, fadeblack, dissolve, wipeleft, slideup, circleopen, clock,
zoom, pixelize, blur and others, as kind:duration, with an equal-power crossfade of the sound. overlay --key green
keys out a green screen and takes the spill out of its edges; --key auto reads the screen colour from the overlay's first frame. --save-edit writes the edit as JSON, which edit renders,
and which can be changed by hand: effects in it are egami run steps, such as "blur 8".
egami-video join in.avi in.webm in.mkv --transition dissolve:0.5 --size 320x240 -o joined.mkv
egami-video fade in.webm --in 0.5 --out 1 -o faded.webm
egami-video overlay in.avi in.webm --at right-8,top-8 --scale 0.3 --opacity 0.8 --from 0.5 --to 1.5 -o overlay.mkv
egami-video overlay in.avi in.mkv --key auto --similarity 0.12 --despill 0.6 -o keyed.mkv
egami-video pip in.avi in.webm --corner bottom-left --scale 0.25 --fade-in 0.2 -o pip.mkv
egami-video stack in.avi in.mkv --gap 4 -o side-by-side.mkv --save-edit side-by-side.json
egami-video edit side-by-side.json --codec raw -o again.avi
egami-video speed in.webm 1.5 -o fast.webm
egami-video reverse in.avi -o backwards.mkv
egami-video loop in.avi --count 3 -o looped.mkv
egami-video freeze in.avi --at 0.5 --for 1 -o frozen.mkv
egami-video slideshow shots/*.jpg --each 2 --transition fade:0.5 --pan-zoom --size 320x240 -o show.mkv
speed, reverse, loop and freeze change a clip's timing, picture and sound together; freeze --at end holds the
last frame. slideshow takes images in order (globs sort by name) and moves each with a slow pan and zoom drawn at sub-pixel
accuracy. A step of run can take a time window, @start-end right after it: blur 8 @0:05-0:12 blurs only that part.
egami-video run in.avi grayscale @0.2-0.6 + blur 2 @0.4- + save windowed.mkv
Checking
hash writes a hash of every decoded frame (or every packet with --packets) in the layout of ffmpeg -f framemd5, and the
hashes equal ffmpeg's for the same decoded planes. compare measures PSNR and SSIM per plane between a reference and other
clips, and --min-psnr or --min-ssim turn it into a check that exits with 1. verify checks FFV1 files for damage without
decoding a picture: every slice's CRC and footer and the configuration CRC, far faster than a decode, so an archive can run it
routinely; it exits with 1 when a file is damaged and lists each damaged slice (--json for scripts).
egami-video hash in.webm --total
egami-video compare in.mkv small.mkv --scale --json
egami-video encode in.mkv --codec ffv1 -o archive.mkv
egami-video verify archive.mkv
Files, batches and pipes
Inputs are files, globs (*, ?, [abc], **, expanded the same way in every shell), folders with --recursive, or - for
standard input. Outputs are a file, a folder, a template with {name}, {ext}, {dir}, {index}, {format} and the
command's part ({frame}, {part}, {scene}, {track}), or - for standard output with --format. Several inputs run at
once (--jobs), a failed file is reported and the rest go on unless --fail-fast, and an output never replaces its input
without --in-place.
Standard input is read as it comes and standard output written as it goes, so egami-video sits between other programs without temporary files. Matroska, WebM, IVF, Y4M and WAV stream; AVI needs a file.
egami-video remux - -o - --format mkv < in.avi > piped.mkv
Progress and stopping
A long job shows a line on standard error when it is a terminal: time, percent, frames, frames per second, size, speed and time
left. --progress <file|-> writes the same reports as ffmpeg -progress does, as key=value blocks ending in
progress=continue or progress=end, or one JSON object per line with --progress-format json. Batches report each file.
--quiet turns the line off.
Ctrl+C stops the job between packets: the output it was writing is deleted, and the exit code is 130. Outputs are written under a temporary name and get their own name only when complete.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Done |
| 1 | A file failed (unreadable, corrupt, unwritable), or compare missed --min-psnr or --min-ssim |
| 2 | The command line is wrong; the message says what to change |
| 3 | An input exceeded a safety limit (--limit, EGAMI_LIMITS) |
| 130 | Stopped with Ctrl+C |