ffdrop
A drag-to-encode frontend for ffmpeg and sips. It gathers the transcodes you run over and over in video/animation production — encode, ProRes intermediates, color-tag fixes, range conversion, audio extract, PNG sequences — and now image conversion with ICC profile handling, behind a drop surface instead of a terminal window.
Presets
Each preset maps directly to one ffmpeg call. No hidden heuristics.
| Preset | Codec / profile | Pixel format | Audio | Container |
|---|---|---|---|---|
| Video → MP4 (h264) | libx264 · profile main · CRF (default 20) |
yuv420p |
aac 192k / -an |
.mp4 +faststart |
| Video → MP4 (h264, fast/HW) | h264_videotoolbox · -q:v (hardware) |
yuv420p |
aac 192k / -an |
.mp4 +faststart |
| Video → MP4 (HEVC/h265) | libx265 · CRF · -tag:v hvc1 |
yuv420p |
aac 192k / -an |
.mp4 +faststart |
| Video → MP4 (HEVC, fast/HW) | hevc_videotoolbox · -q:v · hvc1 |
yuv420p |
aac 192k / -an |
.mp4 +faststart |
| Video → ProRes 422 Proxy | prores_ks · -profile:v 0 |
yuv422p10le |
pcm_s16le |
.mov |
| Video → ProRes 422 LT | prores_ks · -profile:v 1 |
yuv422p10le |
pcm_s16le |
.mov |
| Video → ProRes 422 HQ | prores_ks · -profile:v 3 |
yuv422p10le |
pcm_s16le |
.mov |
| Video → WAV (extract) | -vn · pcm_s16le |
— | passthrough → PCM | .wav |
| Video → MP3 (extract) | -vn · libmp3lame -q:a 2 |
— | VBR ~190 kbps | .mp3 |
| Video → Fool QuickTime gamma | -c copy · -color_trc iec61966-2-1 + h264_metadata transfer=13 |
passthrough | passthrough | .mp4 +write_colr |
| Video → Undo gamma hack | -c copy · -color_trc bt709 + h264_metadata transfer=1 |
passthrough | passthrough | .mp4 +write_colr |
| Image sequence → MP4 h264 | libx264 · CRF 20 |
yuv420p |
— | .mp4 +faststart |
| Image sequence → ProRes 422 HQ | prores_ks · -profile:v 3 |
yuv422p10le |
— | .mov |
ProRes presets set -vendor apl0. Image sequences (PNG/JPEG/TIFF) are detected by a
numbered pattern in the dropped folder (first type wins); framerate is chosen in the UI and fed as
-framerate. HW presets encode via Apple VideoToolbox (fast, -q:v
instead of CRF); HEVC gets the hvc1 tag for QuickTime playback.
Color-tag & range
Rewrite color metadata without re-encoding, or convert range at the pixel level.
Per-file override of primaries / transfer / matrix / range, read from
ffprobe and shown in the info panel.
Metadata-only (lossless)
Rewrites the color atoms with -c copy — no new encode, the bitstream
is untouched. Fixes mistagged transfer/primaries/matrix/range when the tag is wrong
rather than the pixel data. (For the Apple-specific ~1.96 gamma shift, see the QuickTime hack below.)
ffmpeg -i in.mov -c copy \
-color_primaries bt709 \
-color_trc bt709 \
-colorspace bt709 \
-color_range tv \
in_retagged.mov
Range rescale (destructive)
When the pixel values are actually mis-scaled: tick pixel rescale and a
scale step is added to the filter chain. tv → limited, pc → full,
in_range is auto-detected.
-vf scale=in_range=auto:out_range=limited
The flags map 1:1 to ffmpeg: -color_primaries /
-color_trc / -colorspace / -color_range.
QuickTime gamma (ugly hack)
macOS ColorSync (QuickTime, Preview, spacebar in Finder, Safari, Chrome) renders
Rec.709 at Gamma ~1.96 — flatter and lighter than 2.2–2.4. VLC and editors show it
correctly. Fool QuickTime gamma re-tags transfer to sRGB (13) on both
the container colr atom and the H.264 VUI flag, so ColorSync lands on
the ~2.2 curve. The pixels are untouched. It's a zero-sum trade, not a fix: the file then looks
wrong in VLC / Premiere / AE / DaVinci. One and the same file can't look
identical in both camps. Undo gamma hack writes transfer back
to bt709 — a bit-identical round trip. h264 only; for Mac viewing only, never a master.
# fool: container + bitstream → sRGB
-c copy -color_trc iec61966-2-1 -color_primaries bt709 -colorspace bt709 \
-movflags +faststart+write_colr \
-bsf:v h264_metadata=transfer_characteristics=13:colour_primaries=1:matrix_coefficients=1
See the command as you build it
Every option you touch in the UI assembles the actual ffmpeg command in real time — so you can watch it take shape before anything runs. Flip a toggle and the exact flag appears; a typical h264 command ends up looking something like this:
ffmpeg -y -hide_banner -i in.mov \
[-vf scale=trunc(iw/4)*2:trunc(ih/4)*2] # "scale 50%"
[-vf scale=in_range=auto:out_range=limited] # range rescale
-c:v libx264 -crf N -preset effort \
-profile:v main -pix_fmt yuv420p -movflags +faststart \
[-color_primaries … -color_trc … -colorspace … -color_range …] \
[-tune grain] # "preserve grain"
[-c:a aac -b:a 192k | -an] \
out_h264_crfN.mp4
The output filename encodes the choices too: _h264_crf20_50_grain_noaudio_colortagged.
The scale expression truncates to even dimensions (an h264 requirement).
-preset is chosen in the UI (encoder effort, ultrafast…veryslow) — it trades
compression time for file size, not quality (CRF controls quality).
Progress is read from -progress pipe:1 against the frame count from ffprobe,
with a live ETA while the job runs.
Image & ICC
Still images go through sips (not ffmpeg — which handles ICC profiles poorly).
Drop in PNG / JPEG / TIFF / HEIC / WebP, convert to any output format, with a
per-image ICC action. No new dependencies — sips ships with macOS.
| Target | sips format | Quality | Alpha | 16-bit |
|---|---|---|---|---|
| Image → PNG | png | lossless (DEFLATE) | preserved | preserved |
| Image → JPEG | jpeg | formatOptions 1–100 (def 95) | flattened against white | — |
| Image → TIFF | tiff | lossless | preserved | preserved |
| Image → HEIC | heic | sips default | preserved | preserved |
WebP is read as input but not written (sips has no webp encode). File extensions map to
sips format names: jpg→jpeg, tif→tiff.
Four ICC actions
Keep / Assign
Keep leaves the source profile untouched. Assign embeds a
chosen profile without touching the pixels (sips --embedProfile) — like Photoshop's
"Assign Profile". The profile list is filtered to embeddable RGB profiles (the ICC header is read;
CMYK/Gray/Lab/abstract tone profiles are filtered out).
sips -s format jpeg \
--embedProfile "…/sRGB Profile.icc" \
in.png --out in_sRGB.jpg
Convert / Strip
Convert recomputes the pixels between profiles with a rendering intent
(--matchToWithIntent; perceptual/relative/saturation/absolute).
Strip tears the color marking down to a physically bare file via custom
PNG-chunk and JPEG-marker surgery (not just sips, which always re-embeds a default).
sips --matchToWithIntent \
"…/Display P3.icc" relative \
in.png --out in_DisplayP3.png
Pitfalls ffdrop warns about in the UI: Convert downsamples 16-bit → 8-bit
(Assign preserves the depth). JPEG has no alpha — transparency is flattened against white (choose PNG/TIFF/HEIC
to keep it). The info panel reads the raw file, not sips -g profile
(which reports default sRGB even on a profile-less file). The output name encodes profile + quality:
image_sRGB_q95.jpg.
Bit depth (8 ↔ 16)
Bit depth is shown right on the file row (pill) and in the info panel — for single images and
PNG sequences (read from the first frame). Changing bit depth goes through
ffmpeg (sips can't), alpha-aware: rgb24/rgb48be and
rgba/rgba64be respectively. PNG/TIFF only. Since ffmpeg doesn't carry the ICC profile,
bit depth and ICC action are mutually exclusive — choose a bit depth and ICC greys out, with the app warning
that the profile will be stripped. Output name: image_16bit.png.
# 16-bit → 8-bit (no alpha)
ffmpeg -y -i in.png -pix_fmt rgb24 in_8bit.png
# 8-bit → 16-bit (with alpha)
ffmpeg -y -i in.png -pix_fmt rgba64be in_16bit.png
Workflow
The details that make batch runs predictable in real production.
Copy the command
Every file has a Cmd panel that shows the exact
ffmpeg/sips argv that runs — the same line, shell-quoted, ready to
paste straight into Terminal. The panel can stay pinned open and updates
live as you tweak preset, CRF, encoder effort, color/range, bit depth or ICC.
No guessing: what you see is what runs.
Output collision protection
Two jobs never overwrite each other — or an existing file. ffdrop reserves
the output path and picks the first free variant
(name, name (2), name (3)…), accounting for both
disk and other queued jobs. Drop a whole batch without losing files.
Folder drop expands
Drop a folder and it's scanned (one level), and every video/image becomes its own row — as if they'd been dragged in individually. A folder that is an image sequence is treated as a sequence instead. Junk and dotfiles are skipped.
Target size & 2-pass
Drive h264/HEVC with CRF (constant quality) or a target size in MB —
ffdrop works out the bitrate from the video length (-b:v, audio budget subtracted). To
hit the target size precisely: tick 2-pass (software encoder) — an analysis pass
followed by an encode pass, with separate progress for each. Scale to any height (scale=-2:N,
width auto).
Requirements
- macOS on Apple Silicon (arm64). No universal binary for now.
- The system
ffmpeg+ffprobein PATH (for video/PNG sequences). ffdrop doesn't bundle them — it looks in/opt/homebrew/bin,/usr/local/bin,/opt/local/bin, thenwhich. - Image conversion uses
sips, which ships with macOS — nothing extra to install. - If they're missing, the app shows a setup view and polls every 3 seconds until they appear.
Install via Homebrew:
brew install ffmpeg. - Signed (Developer ID) and notarized — no Gatekeeper override needed.