FastForward
What it is, how it is built, and what formats it supports
The Amiga personal computer is the greatest multimedia computer ever made and as such deserves a full-fledged pluggable multimedia framework at the operating system level — shared infrastructure for playback, editing, streaming, and format handling that any application can build on.
Apple had QuickTime, BeOS its Media Kit, even Windows had DirectShow.
Now it's time for Amiga to catch up with FastForward
FastForward from amigazen project expands upon the foundations provided by realtime.library and datatypes.library to provide a native, performance optimised multimedia subsystem — a LoadSeg-pluggable pipeline from byte stream to pixels and samples, with QuickTime/MP4-style multimedia container parsing and a BeOS-style filter graph for editors, sequencers and transcoders.
FastForward comprises:
fastforward.library — the shared engine: plugin registry, pipeline and filter graph, movie and graph APIs, sniffing, buffer pools, clocks, events, probe, stats, metadata and thumbnail support
Play — Universal media player tool (Source/play/)
Examine — Shell command media inspector; reports containers, streams, and which plugins would bind to the file being examined
player.gadget and playback.gadget — BOOPSI gadget classes for embedding a media player and its playback controls in Intuition applications
Built-in plugins
mem.source — in-memory byte streams
callback.sink — delivers frames to application callbacks
FastForward plugins
Sources — file.source for local files, http-live.source for HTTP-LS streams, rtsp.source for RTSP streams
Demuxers — pcm.demux, mov.demux, avi.demux, cdxl.demux, au.demux, fpadpcm.demux, rm.demux, mpegaudio.demux, mpeg.demux, ts.demux, ogg.demux, flac.demux, mkv.demux
Codecs — pcm.codec, ima4.codec, fpadpcm.codec, msadpcm.codec, dviadpcm.codec, mace.codec, mpegaudio.codec, mpegvideo.codec, vorbis.codec, cvid.codec, svq1.codec, msvc.codec, rpza.codec, smc.codec, qtrle.codec, cdxl.codec, ra144.codec, ra288.codec, a52.codec, aac.codec
Filters — volume.filter, fade.filter
Sinks — audio.sink, ahi.sink, video.sink, ham.sink, null.sink
amigazen project
A web, suddenly
Forty years meditation
Minds awaken, free
amigazen project is using modern software development tools and methods to update and rerelease classic Amiga open source software. Projects include a new AWeb, a new Amiga Python 2, and the ToolKit project — a universal SDK for Amiga development. All amigazen project releases are guaranteed to build against the ToolKit standard so that anyone can download and begin contributing straightaway without having to tailor the toolchain for their own setup.
FastForward is an original work of the amigazen project, released under the BSD 2-Clause License.
The amigazen project philosophy is based on openness:
Open to anyone and everyone — Open source and free for all — Open your mind and create!
PRs for all projects are gratefully received at GitHub. While the focus now is on classic 68k software, it is intended that all amigazen project releases can be ported to other Amiga-like systems including AROS and MorphOS where feasible.
About FastForward
fastforward.library is a standalone multimedia engine for classic Amiga. It performs format sniffing, container demuxing, codec decode, and device output through a linear or arbitrary filter graph. It does not replace datatypes.library for still images — PNG, ILBM, JPEG, and GIF stay on datatypes. FastForward owns timed media, and in fact implements the backend needed for the movie.datatype rootclass.
dos.library, utility.library, and realtime.library (for Conductor-backed clocks) open on-demand inside the engine. Codec backends are plugin-owned — the core library opens none of them at init time. Plugins can be self-contained or defer to optional Exec shared libraries opened lazily through FFHost.
Inspirations
FastForward stands on decades of Amiga and cross-platform multimedia work. The design deliberately combines ideas that already proved themselves elsewhere, expressed in Amiga-native primitives:
| Inspiration |
What we took from it |
| BeOS Media Kit (1998) |
Node-per-thread topology, BBuffer flowing between nodes on kernel ports, a master time source anchoring the graph. FastForward maps this onto Exec Tasks, Message/PutMsg/ReplyMsg, and realtime.library Conductors — the architecture BeOS got right, on primitives Amiga has had since 1985. |
| MacOS QuickTime |
The Movie / Track model for player apps: one high-level FFMovie handle, per-track mute/volume/sink control, and metadata introspection without exposing the underlying graph. |
| AmigaOS realtime.library |
Conductor + Player as the master clock for sequencers and NLE timelines; per-element Players slaved to one graph Conductor for sample-accurate multi-track sync. |
Two metaphors read the architecture out loud. Orchestral language fits the timing layer (Conductor, Player, tempo, ticks). Tape-deck language fits the application boundary (Play, Pause, Stop, scrub, and FastForward).
Contact
At GitHub https://github.com/amigazen/FastForward/
On the web at http://www.amigazen.com/fastforward/ (Amiga browser compatible)
Or email fastforward@amigazen.com
Acknowledgements
Amiga is a trademark of Amiga Inc.
Architecture
Overview
FastForward is a pluggable, pipelined Amiga-native multimedia engine for AmigaOS 3 (68k). Applications such as Play link against fastforward.library and never load codec engines directly. Demuxers, codecs, filters, sources, and sinks are LoadSeg plugin modules whose directory is resolved at init time (see the User’s Guide).
Tiers
| Tier |
Component |
Role |
| 1 |
fastforward.library |
Registry, linear pipeline, sniffing, buffer pool, host services |
| 2 |
LoadSeg plugin |
Source, demux, codec, filter, sink entry points |
| 3 |
Exec libraries |
Optional heavy backends (e.g. mpega.library) opened via host fh_OpenLibrary |
| 4 |
Devices |
ahi.device, future CGX/P96 video |
Pipeline (MVP)
Linear chain built by BuildFFPipelineFromFile():
file source → PCM demux → PCM codec → AHI sink |
Data flows as FFBuffer records. Ring buffers default to 64 KiB per edge (FF_DEFAULT_RING). Fast RAM is used for engine/plugin instance state and streaming buffers; chip RAM is reserved for AHI sample buffers in ahi.sink.
Plugin ABI
Headers: Source/include/libraries/ffplugin.h (plugins) and Source/include/libraries/fastforward.h (applications).
Each plugin binary begins with FFPluginHead (ph_Security = 0x70FF4E75, ph_ID = 'FFPI'). One or more FFPlugin structures follow. The loader (Source/library/ff_loader.c) scans LoadSeg segments and registers plugins by kind and identifier.
Per-instance state lives in FFInstance + plugin-private data allocated through FFHost::fh_Alloc. Plugins must not keep mutable static globals.
Host services
FFHost provides allocation, logging, and optional OpenLibrary/CloseLibrary. All plugins receive the same vtable at Open().
Built-in plugins
For development, FFRegisterBuiltinPlugins() registers symbols linked into fastforward.library. Production installs use external plugin files only.
Supported formats and plugins
FastForward discovers and loads plugins from LIBS:FastForward/Plugins/ (or the assign / ENV paths described in the User’s Guide). Each plugin registers an identifier and kind; the engine scores candidates during sniff and probe to build a pipeline for the media at hand.
Plugin kinds
Sources (FFK_SOURCE) — byte streams from files, memory, HTTP-LS, or RTSP
Demuxers (FFK_DEMUX) — container parsers that split streams
Codecs (FFK_CODEC) — audio and video decoders (and future encoders)
Filters (FFK_FILTER) — in-graph transforms such as gain and fade
Sinks (FFK_SINK) — AHI audio, Intuition video, callbacks, or null output
Muxers (FFK_MUX) — transcode and export (planned)
3. Implemented (MVP)
These four plugins plus the callback sink (built into the engine) form the current vertical slice: WAV / AIFF / 8SVX / 16SV → AHI.
| Plugin |
Kind |
Source |
Notes |
| file.source |
FFK_SOURCE |
FastForward Source/plugins/file_source.c |
BPTR / path byte reader; all file-based chains start here |
| (built-in) mem.source |
FFK_SOURCE |
Source/plugins/mem_source.c |
Memory buffer reader for previews and embedded media |
| pcm.demux |
FFK_DEMUX |
same + sniff logic in Source/library/ff_sniff.c |
WAV, AIFF, 8SVX, 16SV IFF containers |
| pcm.codec |
FFK_CODEC |
same |
Pass-through PCM (identity codec); format negotiation |
| ahi.sink |
FFK_SINK |
same |
AHI output via ahi.device; chip RAM sample buffers |
| (built-in) callback.sink |
FFK_SINK |
Source/library/ff_callback_sink.c |
Delivers frames to app FFCallbacks; not a LoadSeg file |
18. Implemented plugins (status)
These plugins live in Source/plugins/ and build via its smakefile. The list grows as plugins land in tree.
| Plugin |
Kind |
Status |
Capabilities |
| file.source |
FFK_SOURCE |
done |
Local file byte reader |
| pcm.demux |
FFK_DEMUX |
done |
IFF / WAV / AU container detection |
| pcm.codec |
FFK_CODEC |
done |
Endian-swap + bit-depth pass-through |
| ahi.sink |
FFK_SINK |
done |
AHI double-buffered playback |
| au.demux |
FFK_DEMUX |
done |
Sun / NeXT .au (S8 / S16BE) |
| null.sink |
FFK_SINK |
done |
Bit-bucket sink for benchmarking |
| volume.filter |
FFK_FILTER |
done |
PCM gain (S8 / S16 / U8) |
| mpegaudio.demux |
FFK_DEMUX |
done |
MP1 / MP2 / MP3 elementary stream framing, ID3v2 skip |
| mpeg.demux |
FFK_DEMUX |
done |
MPEG-1 / MPEG-2 Program Stream (audio + video PES split) |
| mpega.codec |
FFK_CODEC |
planned |
MPEG audio Layers I–III decode |
| helixmp3.codec |
FFK_CODEC |
planned |
MP3 Layer III decode (68k-optimised path) |
| mpeg2.codec |
FFK_CODEC |
planned |
MPEG-1 / MPEG-2 video decode |
Two metaphors that read the architecture out loud
FastForward leans on two metaphors that are already baked into the primitives we use, and they map cleanly onto two different reader populations.
Orchestral — for the timing layer
realtime.library itself is named for an orchestra, and our timing code follows suit. Authors of CAMD / OctaMED / Bars&Pipes plugins already think in these terms.
| Orchestra |
realtime.library |
FastForward |
| Conductor |
struct Conductor |
FFClock.fc_Conductor (the master timing source) |
| Player on the podium |
struct Player, CreatePlayerA |
FFClock.fc_Player plus per-element FFElement.fe_Player (each producer has its own Player slaved to the graph's Conductor) |
| Tempo marking on the score |
PLAYER_Tempo |
FFClock.fc_Tempo (BPM × 1000) |
| Beat / tick subdivision |
PLAYER_Ticks |
FFClock.fc_TickRate |
| External sync source (a click track, SMPTE feed, MIDI clock) |
PLAYER_ExtSync, ExternalSync() |
FFCT_ExternalSync + FFCT_PlayerHandle; see API.md §19 |
| The score itself |
(no equivalent) |
FFGraph — the rehearsed plan |
| One performer |
(no equivalent) |
FFElement — one player of one part |
| Their instrument's range |
(no equivalent) |
FFPad.fp_Caps, fp_Audio, fp_Video |
We deliberately do not rename FFGraph / FFElement / FFPad to orchestral terms. The framework is multi-medium — a video frame is not really a "phrase" — and Player / Conductor are already realtime.library types we would shadow. The metaphor names why we do what we do, not what we call our types.
Tape-deck — for the transport layer
The library is named after the FastForward button. The application- facing transport is, by design, exactly the buttons on a real tape deck. Docs/API.md §3 walks through these in code, but the mental model is:
| Tape deck button |
FastForward call |
| Play |
PlayFFMovie() |
| Pause |
PauseFFMovie() |
| Stop |
StopFFMovie() |
| Cue / scrub |
SeekFFMovie(time, flags) |
| Rewind to head |
RewindFFMovie() (macro: Stop + Seek 0) |
| Eject the tape |
EjectFFMovie() (alias of CloseFFMovie()) |
| Resume from pause |
ResumeFFMovie() (alias of PlayFFMovie()) |
| Fast-forward 2x |
FastForwardFFMovie() (sets FFMT_PlayRate = 2.0) |
| Reverse 1x |
ReverseFFMovie() (sets FFMT_PlayRate = -1.0) |
| Variable scan |
ScanFFMovie(rate) (signed Q16.16 — FF_RATE_FF_4X etc.) |
| Loop / autoreverse |
FFMT_LoopMode = FFLM_LOOP / FFLM_PINGPONG / FFLM_REGION |
These are deliberately macros, not new LVOs. They compose existing LVOs (PlayFFMovie, SeekFFMovie, SetFFMovieAttrsA) so the ABI surface stays small, and any plugin that honours FFMT_PlayRate participates automatically. Plugins that only support 1× playback silently stay at 1× and the engine posts FFEV_QOS so the UI can grey out the affordances that are not supported by the current codec.
The two metaphors complement each other: orchestral language is right at the timing boundary where FastForward already speaks realtime.library; tape-deck language is right at the application boundary where humans drive the deck. Inside the library we use neither — FFGraph, FFElement, FFPad, FFBuffer are technical names for technical concepts.
Scheduling decisions. Amiga unified memory means the framework can pass references everywhere except the final sink hand-off. The memory class hint (FFBM_FAST / FFBM_CHIP / FFBM_LOCAL / FFBM_ANY) on each FFBuffer keeps the decoder/filter chain in Fast RAM where the 68k is fastest, and only the AHI sink's DMA buffer lives in Chip RAM where Paula can read it without the CPU. Video sinks follow the same rule: chunky-to-planar conversion happens once into a Chip-RAM bitmap that the blitter and copper then consume directly.
Run modes. Every FFElement defaults to FFER_INLINE, so single-thread linear graphs (the MVP audio path) keep their existing behaviour with zero overhead. Heavy elements opt into their own Task by passing FFE_RunMode = FFER_TASK; sources and sinks that need dos.library calls use FFER_PROCESS (a CreateNewProc so Lock, Read, OpenDevice all work). Mixed graphs are normal: Play stays single-thread for cheap WAV decode; an NLE timeline runs each track as its own Task; a music sequencer attaches every producer to the master Conductor so audio, MIDI and video track heads stay sample-accurate.
Topology granularity on classic 68k. Exec context switching is cheap by 2026 standards — sub-millisecond on a stock 68030 — but it is not free, and a graph that puts every filter on its own Task spends measurable cycles on Signal / PutMsg / WaitPort per buffer. The recommended pattern on classic hardware is coarse- grained: keep FFER_INLINE as the aggressive default so a single caller-task slice runs Pull → Decode → Filter sequentially, and cross to FFER_TASK / FFER_PROCESS only at boundaries where you genuinely want the asynchronicity — disk I/O, the AHI / video sink, a CPU-heavy codec that wants its own pacing. Long pipelines of fine-grained nodes are an RTG / 68060+ luxury; on a stock 68030 they trade frame-rate for thread-count and lose. The graph builder honours whatever the application requests; it does not silently fold elements together.
Why not real process isolation. AmigaOS 3 has no MMU separation between Tasks; a buggy codec can scribble on the engine no matter how many message ports we put between them. Pretending otherwise would just slow us down. Defensive parsing (counted FFString / FFData everywhere a media file's bytes cross into the engine) is the realistic safety net. WarpOS PPC plugins do get effective crash isolation as a side effect of the PPC kernel boundary — see TODO.md.
See the plugin lists at the top of this page for the full set of registered identifiers. The Developer’s Guide documents the Movie API, Graph API, and BOOPSI gadgets. The Plugin Developer’s Guide covers LoadSeg plugin authoring.
See the User’s Guide for installation and Play. See the Developer’s Guide for the full API reference.
|
Back to top
|
|