README.md

compost

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:

What's in it

Compositing

| Function | Does | |---|---| | 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

| Function | Does | |---|---| | 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

| Function | Does | |---|---| | concat() | Join segments sequentially via the concat demuxer | | crossfade_concat() | Dissolve between clips, optionally overlay one audio track |

Audio

| Function | Does | |---|---| | 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

| Function | Does | |---|---| | probe() | Query video properties via ffprobe | | frame_export() | Pull a single frame at a given time as an image |

Timeline

| Function | Does | |---|---| | 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.



Try the compost package in your browser

Any scripts or data that you put into this service are public.

compost documentation built on Aug. 4, 2026, 5:11 p.m.