Architecture overview

FrameTrellis is a C++20 application with a Qt Quick (QML) interface and an MLT/FFmpeg media engine. The code is split into small libraries with one-way dependencies, so the editing logic can be tested without a GUI and the GUI never talks to MLT directly.

                 ┌──────────────────────────────────────────────┐
                 │ app  (main.cpp)                              │
                 └───────────────┬──────────────────────────────┘
                                 │
                 ┌───────────────▼──────────────────────────────┐
                 │ ui   QML workspace + C++ controllers/models  │  Qt Quick
                 └──┬──────────┬───────────┬───────────┬────────┘
                    │          │           │           │
            ┌───────▼──┐ ┌─────▼────┐ ┌────▼─────┐ ┌───▼──────────────┐
            │ editing  │ │ media    │ │ playback │ │ rendering        │
            │ commands │ │ probe,   │ │ preview  │ │ export settings, │
            │ history, │ │ import,  │ │ engine   │ │ render job ──────┼──► frametrellis-render
            │ document │ │ thumbs   │ │          │ │ (helper process) │    (render runner)
            └───┬──────┘ └────┬─────┘ └────┬─────┘ └────────┬─────────┘
                │             └──────┬─────┘                │
            ┌───▼──────┐      ┌──────▼──────────────┐       │
            │ project  │◄─────┤ engine              │◄──────┘
            │ timeline │      │ MLT runtime, graph  │
            │ core     │      │ builder, stills     │  MLT ─► FFmpeg
            └──────────┘      └─────────────────────┘
LibraryFolderResponsibilityDepends on
ft_coresrc/coreRational, FrameRate, timecode, Result/Error, ids, logging categoriesQt Core
ft_timelinesrc/timelinetracks, clips and validated edit operations, snappingcore
ft_projectsrc/projectmedia assets, project settings, the Project aggregate, the .ftproj formattimeline
ft_editingsrc/editingundoable commands, CommandHistory, ProjectDocumentproject
ft_renderingsrc/renderingexport settings and validation, helper protocol, RenderJobproject
ft_enginesrc/engineMLT lifetime and capabilities, profiles, project → MLT graph, still imagesproject, MLT
ft_mediasrc/mediaprobing, background import, thumbnail cacheengine
ft_playbacksrc/playbackthreaded preview engine, SDL2 audio outputengine
ft_render_enginesrc/renderingRenderRunner and the frametrellis-render executablerendering, media
ft_uisrc/uiQML module FrameTrellis: controllers, models, FrameView, QML fileseverything above
frametrellissrc/appentry point and shutdown orderui

Principles

  • Domain logic has no UI and no MLT. core, timeline, project and editing only use Qt Core and are covered by fast unit tests.
  • Integer time. Positions and durations are integer frame counts at the project frame rate. Frame rates are exact rationals (30000/1001, not 29.97), and conversions to microseconds use overflow-checked rational arithmetic. Clip identity never depends on floating point.
  • Validated edits. Every timeline operation checks its preconditions (overlaps, source length, track kind, locks) and leaves the timeline unchanged when it fails.
  • Undo by snapshots of changed tracks. TimelineCommand runs an edit on a copy, then stores only the tracks that changed (or the whole track list for structural edits). Undo and redo restore those exact states, so they cannot drift.
  • Errors are values. Functions return Result<T>/Status with a user-facing message and technical detail. Exceptions are not used across module boundaries.
  • Untrusted input. Project files and media are treated as hostile: strict JSON validation with size limits, media opened only with the FFmpeg producer (avformat: prefix), no shell commands, no evaluation of document content.
  • Never touch user media. Media are referenced by path; FrameTrellis never moves, edits or deletes them. Exports refuse to overwrite source files or the project.

Threads

ThreadOwns
GUI threadQML, controllers, ProjectDocument, RenderJob (QProcess)
Playback workerthe preview's MLT profile, graph builder and tractor; renders frames and audio
Thread poolmedia probing and thumbnails (each task creates its own MLT objects)
Render helper processthe export's MLT graph and encoder

Rules: MLT objects never cross threads; the playback engine receives immutable Project snapshots; results from background tasks carry a generation number or are delivered through QFutureWatcher children so they are dropped if their owner is gone. On shutdown the QML engine is destroyed first, then the controllers (stopping playback and the render helper), then the thread pool is drained, and only then is MLT closed.

See also: project format, playback, rendering.