← Files Cecil-IA Labs FFmpegARCHIVED FILE

docs/development/composition-and-filter-graph.md

2.59 KB · Sep 30, 2026 · 23:17 UTC

↓ Download file

# Milestone 7 — Composition & Filter Graph Engine

Milestone 7 introduces a reusable composition layer instead of embedding ad-hoc `-filter_complex` strings in individual scripts.

## Public commands

```bash
cecilia-ffmpeg compose concat <inputs...>
cecilia-ffmpeg compose transition <left> <right>
cecilia-ffmpeg compose slideshow <directory>
```

## Filter graph architecture

`FilterGraphBuilder` owns graph-chain assembly. Video normalization is centralized in `normalization.ts` and resolves a common width, height, FPS, pixel format, timebase, and zero-based timeline before `xfade`.

For FFmpeg 7.x compatibility, `setpts=PTS-STARTPTS` is intentionally applied **before** the `fps` filter. Applying `setpts` after `fps` can clear the negotiated constant frame-rate metadata and cause `xfade` to fail with `current rate of 1/0 is invalid`.

The normalized sequence is therefore:

```text
scale
→ pad
→ setpts=PTS-STARTPTS
→ fps
→ format=yuv420p
→ settb=AVTB
→ xfade/concat
```

This still satisfies the core invariant: all inputs reach `xfade` with matching geometry, constant frame rate, pixel format, timebase, and zero-based timestamps.

## Concat

`compose concat` accepts two or more inputs. Without a transition it uses FFmpeg's `concat` filter after normalization. With `--transition`, it builds an N-input `xfade` chain with cumulative offsets.

```bash
cecilia-ffmpeg compose concat a.mp4 b.mp4 c.mp4 \
  --transition fade \
  --transition-duration 1 \
  --fps 30 \
  --output final.mp4
```

Audio policy:

- `auto` preserves audio only when every input has audio;
- `preserve` requires audio on every input;
- `drop` produces video-only output.

Transitioned audio uses sequential `acrossfade`; non-transition concatenation uses the audio `concat` filter.

## Transition

`compose transition` composes exactly two videos via `xfade` and, when audio is preserved, `acrossfade`.

```bash
cecilia-ffmpeg compose transition left.mp4 right.mp4 \
  --transition dissolve \
  --duration 0.75 \
  --output result.mp4
```

Supported initial transitions are `fade`, `fadeblack`, `fadewhite`, `wipeleft`, `wiperight`, `slideup`, `slidedown`, `circleopen`, `circleclose`, `dissolve`, `pixelize`, and `distance`.

## Vertical-stack slideshow

The legacy Taner Sener script is migrated as a typed vertical-stack slideshow implementation. Image discovery is deterministic and supports JPEG, PNG, WebP, and BMP.

```bash
cecilia-ffmpeg compose slideshow ./images \
  --width 1280 \
  --height 720 \
  --duration 10 \
  --direction up \
  --output slideshow.mp4
```

The implementation no longer uses shell string concatenation or `eval`.

SHA-256: e85beed70417d53b470109b5a38293fa4a763bb623b3a4fce387028dbabe072a