compost

Video Compositing via 'FFmpeg'

Last updated: 2026-07-20

Video compositing via FFmpeg, wrapped in clean R functions.

compost turns common ffmpeg filter_complex patterns (overlay, chromakey, concat, scale, stack) into small, composable R functions, and lowers an OpenTimelineIO timeline (through rotio) to a single rendered video.

Every function shells out to ffmpeg/ffprobe. Nothing is reimplemented in R; the package’s job is to build correct filtergraphs so you don’t have to.

Install

remotes::install_github("cornball-ai/compost")

Requires ffmpeg (>= 5.0) on your PATH. The only R dependency is rotio.

Conventions

A few patterns hold across the API:

  • Output format is inferred from the output file’s extension.
  • overwrite = TRUE (default) passes -y to ffmpeg.
  • dry_run = TRUE returns the ffmpeg command string instead of running it, so you can inspect or test the filtergraph without rendering.
  • Functions return the output path invisibly.

What’s in it

Compositing

FunctionDoes
overlay()Composite a PNG (with alpha) or video over a background
pip()Picture-in-picture a clip into a corner
chromakey()Key out a colored background and composite
colorkey()Key out a flat background, write ProRes 4444 (alpha preserved)
hstack()Stack two videos side by side
vstack()Stack two videos vertically
pad()Scale to fit and pad onto a canvas
zoom()Stepped zoom that changes level at STT line boundaries
compose_layout()Paint N streams into pixel-rect slots on a canvas (heads over slides, stacks, over-shoulder boxes)

Stills and sequences

FunctionDoes
still_clip()One image → a video clip, static or Ken Burns pan/zoom
frames_clip()A numbered frame sequence → a video clip

Joining and transitions

FunctionDoes
concat()Join segments sequentially via the concat demuxer
crossfade_concat()Dissolve between clips, optionally overlay one audio track

Audio

FunctionDoes
audio_convert()Re-encode / resample / remix to a new container or codec
audio_concat()Join audio files with exact silence gaps (decoded-domain, one encode)
normalize_audio()Single EBU R128 loudnorm pass
broadcast_audio()Broadcast-clean chain: high-pass, de-hum, loudness, fades
align_audio()Locate a clip’s audio within a longer narration
align_overlaps()Place chunks on the narration clock and derive overlaps
subclip()Cut a [start, end] range out of a media file
rms_curve()Per-block RMS levels across a file

Inspection and frames

FunctionDoes
probe()Query video properties via ffprobe
frame_export()Pull a single frame at a given time as an image

Timeline

FunctionDoes
render_timeline()Lower a rotio (OTIO) timeline to a rendered video: source-range trims, dissolve transitions, still/sequence pre-render, cornball.* motion effects, multi-track slot layouts, caption burn, and a narration bed the video locks to

The cornball.* effect namespace and the slot-layout contract are documented in inst/schema/cornball-effects.md and inst/schema/cornball-layout.md.

Examples

Picture-in-picture a webcam into the bottom-right of a screen recording:

library(compost)

pip("screen.mp4", "webcam.mp4", "out.mp4",
    scale = 0.25, corner = "lower-right")

Convert a meeting recording to STT-ready audio in one pass:

audio_convert("recording.mp4", "speech.wav",
              sample_rate = 16000, channels = 1)

Crossfade a sequence of clips and lay one music bed over the top:

crossfade_concat(c("a.mp4", "b.mp4", "c.mp4"), "reel.mp4",
                 fade = 0.5, audio = "music.mp3")

Inspect the ffmpeg command without rendering (returns the command string):

overlay("bg.mp4", "logo.png", "out.mp4", x = 40, y = 40, dry_run = TRUE)

License

MIT. Copyright cornball.ai.

Functions