rendering library

The pipeline surface behind Fluvie's renderers: capture services, render sandboxes, encoding seams, media-resolver contracts, and the renderer entry points they compose.

Import it where frames are produced — a render harness, an encoder backend, a server — never inside a composition:

import 'package:fluvie/rendering.dart';

Compositions import only package:fluvie/fluvie.dart, the authoring surface. The two barrels are disjoint: authoring stays free of pipeline machinery, and this surface assumes you are building or hosting a renderer.

Classes

BeatDetectionService
Detects the beats of one audio track and emits them as a BeatGrid in absolute video-frame space.
CaptureSink
A growable byte sink the capture loop appends raw frame bytes to.
DesktopVideoRenderer
Renders a Fluvie composition to an MP4 file with a local FFmpeg.
DisposableResolver
A media resolver that holds disposable native resources (decoded images) and can release them eagerly instead of waiting for garbage collection.
FadeBox
The render-safe fade primitive: one named place where every Fluvie fade becomes pixels.
FfmpegRunner
An FFmpeg runtime that can probe its version and run one encode from a pre-built argument array.
FfmpegRunnerRegistry
Chooses the FfmpegRunner for a platform: the native process provider everywhere except the web, where ffmpeg.wasm takes over.
FfmpegVersion
An FFmpeg major.minor version parsed from a -version banner, with the >= 6.0 floor this library requires.
FileRenderSandbox
A file-backed RenderSandbox under a real directory.
FrameCache
Persists captured frames on disk so an unchanged render replays from cache instead of re-capturing.
FrameCacheStore
Adapts a disk FrameCache to the dart:io-free FrameStore the capture loop reads, computing each key from the render digest and frame index.
FrameCaptureService
Captures the pixels of one already-pumped frame as a RawFrame.
FrameExtractionService
Extracts one decoded frame of a video source as a RawFrame.
FrequencyAnalyzer
Analyses one audio track into a per-frame BandTable driving spectrum-reactive animations.
GenerativeResolver
Produces every declared generative source before the media pre-pass and serves the resolved local source synchronously afterwards.
MediaResolver
Pre-resolves every media source before the frame loop and serves the resolved bytes (and decoded images) synchronously during it.
MemoryRenderSandbox
An in-memory RenderSandbox: every file lives in a map of bytes.
NetworkAllowlist
The network safety gate consulted before any media fetch.
NoGenerativeResolver
The generation-less GenerativeResolver: valid only for compositions that declare no AI-generated media.
NoMediaResolver
The media-less MediaResolver: valid only for compositions that declare no media at all.
RawFrame
One captured frame: raw RGBA8888 pixels plus the metadata the encoder needs to place them in the stream.
RenderConfig
Everything one render needs to know: output size, frame rate, the frame window to capture, encode quality, and whether the frame cache may serve repeated frames.
RenderManifest
The capture sandbox's completion signal and encode plan, written last (after the frames file is complete) as manifest.json.
RenderProgress
A progress update from a render: the current phase, optional frame counts while capturing, and the compositionKey so concurrent renders stay distinguishable in logs.
RenderSandbox
Storage for one render: the frames file, the manifest, materialized encoder inputs, and the encoded output.
RenderService
Drives the deterministic capture pipeline: pre-resolve media, loop the frames (cache → pump → capture → append), then write the manifest last as the completion signal.
RepaintBoundaryCaptureService
The real FrameCaptureService: rasterizes the RepaintBoundary under the given key via toImage(pixelRatio: 1.0) and reads back raw RGBA bytes.
ResolvedAudioMix
A whole render's audio resolved to encoder-neutral tracks plus a final masterVolume, ready for a non-FFmpeg encoder to mix.
ResolvedAudioTrack
One audio track resolved to encoder-neutral numbers: the source to play plus the timing and gain an encoder needs, with no FFmpeg in sight.
SnapshotService
Rasterizes a SnapshotRequest to a decoded-ready SnapshotRaster — the one boundary every snapshot-backed widget rides.
VideoProbeResult
The probed facts of one video stream.
VideoProbeService
Reads the stream facts of an encoded video file (codec, size, frame count, duration) — what the determinism tests assert against.
VideoRenderer<T>
Renders a composition to T on one platform: a File where the encode lands on disk (desktop, on-device mobile), bytes where it stays in memory (the browser).
WasmRuntime
A loaded ffmpeg.wasm instance with its in-memory virtual file system.
WebClipDecoder
Decodes a video clip in the browser from its bytes — the WebCodecs seam the web MediaResolver probes and extracts clip frames through.

Enums

RenderPhase
The stage a render is in: capturing frames, encoding them, or finished.

Properties

assetBundleProvider → Provider<AssetBundle>
The asset bundle media assets are read from; defaults to rootBundle and is overridable with a fake bundle in tests.
final
ffmpegRunnerProvider → Provider<FfmpegRunner>
The encode provider for the current platform (kIsWeb dispatch), running processes through processRunnerProvider; overridable in tests.
final
frameCacheProvider → Provider<FrameCache>
The frame cache used by the render pipeline; defaults to the shared fluvie_frame_cache directory under the system temp directory and is overridable (for example with a per-test root).
final
frameCaptureServiceProvider → Provider<FrameCaptureService>
The capture service used by the render pipeline; defaults to RepaintBoundaryCaptureService and is overridable in tests.
final
frameExtractionServiceProvider → Provider<FrameExtractionService>
The frame extractor used by the clip render path; defaults to FfmpegFrameExtractionService over processRunnerProvider and is overridable with a fake in tests.
final
generativeResolverProvider → Provider<GenerativeResolver>
The active GenerativeResolver for a render.
final
mediaBytesLoaderProvider → Provider<MediaBytesLoader>
The per-kind byte source the media resolver resolves over; web-safe (the file:// branch is behind a platform seam), so both platform resolvers build over it.
final
mediaResolverProvider → Provider<MediaResolver>
The media resolver used by the native render pipeline: a real MediaRepository over the injected loader plus the clip probe/extraction services, streaming clip frames through an on-disk FileClipFrameStore so a full-resolution or long clip never has to hold every decoded frame in memory, and serving them from the persistent clipFrameCacheProvider so an unchanged clip is not re-extracted on every run. Overridable with a fake in tests; NoMediaResolver stays exported as the deliberate media-less choice.
final
networkAllowlistProvider → Provider<NetworkAllowlist>
The network safety gate consulted before any media fetch; defaults to a host-open, https-only allowlist and is overridable to a strict set.
final
videoProbeServiceProvider → Provider<VideoProbeService>
The video probe used by determinism checks and tooling; defaults to FfprobeVideoProbeService over processRunnerProvider and is overridable in tests.
final

Functions

collectMediaSources(List<Scene> scenes, {GenerativeResolver? generative}) Set<MediaSource>
Gathers every declared MediaSource from scenes before the frame loop — a pure structural walk over the constructor data, with no mounting and no async.
collectSnapshots(List<Scene> scenes) List<Snapshot>
Gathers every Snapshot widget from scenes in deterministic build order — the in-process subtree-capture sibling of collectSnapshotSources, walking the same tree with no mounting and no async.
collectSnapshotSources(List<Scene> scenes) Set<SnapshotSource>
Gathers every declared SnapshotSource from scenes before the frame loop — the snapshot sibling of collectMediaSources, walking the same tree with no mounting and no async.
createWasmRuntime() WasmRuntime
VM stub: there is no ffmpeg.wasm outside the browser.
frameCountFor(Duration duration, int fps) int
The number of frames a render of duration at fps produces (at least one).
gateOptInAudio({required Widget composition, required bool encode, required bool warn, required int fps, required int frameCount, required void warnSink(String message), required String platformLabel, Export? export}) ResolvedAudioMix?
The one audio-drop policy the on-device and in-browser renderers share.
parseAspect(String? aspect) Aspect?
The Aspect for an --aspect value, or null (the composition's declared size wins) for null/empty. Throws an ArgumentError when the name is not an Aspect.
parseExportFormat(String? format) Export?
The Export for a --format value, or null (the mp4 default) for null/empty/mp4.
parsePosterTime(String? poster) Time?
The Time for a --poster value (1.5s, 30f, 500ms), or null (no poster) for null/empty.
parseQuality(String? quality) Quality?
The Quality for a --quality value, or null (the config default) for null/empty. Throws an ArgumentError when the name is not a Quality.
render({required Widget composition, required Aspect aspect, required int frameCount, required Directory outDir, required RenderService service, required ShellMount pumpWidget, required ShellFramePump pumpFrame, int longEdge = VideoDefaults.longEdge, int fps = VideoDefaults.fps, String compositionKey = 'render', bool cacheEnabled = false, AudioMixStager? stageAudio, Iterable<AudioSource>? audioSources, MediaResolver? resolver, GenerativeResolver generative = const NoGenerativeResolver(), void onGenerativeProgress(GenerativeProgress progress)?}) Future<RenderAspectResult>
Renders composition for a single aspect — the canonical multi-aspect entry.
renderTemplate<P>(VideoTemplate<P> template, {required P props, required int frameCount, required Directory outDir, required RenderService service, required ShellMount pumpWidget, required ShellFramePump pumpFrame, Aspect aspect = Aspect.reels, int longEdge = VideoDefaults.longEdge, int fps = VideoDefaults.fps, String compositionKey = 'template', bool cacheEnabled = false}) Future<RenderAspectResult>
Renders a parameterized VideoTemplate for one Props value — the data-driven-batch entry.
renderToSandbox({required Widget composition, required Aspect aspect, required int frameCount, required RenderSandbox sandbox, required FrameCaptureService capture, required SandboxMount pumpWidget, required SandboxFramePump pumpFrame, int longEdge = VideoDefaults.longEdge, int fps = VideoDefaults.fps, String compositionKey = 'render', Export? export, int? posterFrame, ProgressCallback? onProgress, VideoEncoderService encoder = const VideoEncoderService(), List<ResolvedAudioTrack> audioTracks = const [], AudioByteLoader? loadAudioBytes, double audioMasterVolume = 1, MediaResolver? resolver, GenerativeResolver generative = const NoGenerativeResolver(), void onGenerativeProgress(GenerativeProgress progress)?, FrameEncoder? frameEncoder}) Future<RenderManifest>
Renders composition for aspect into sandbox without touching the file system — the in-memory, dart:io-free capture-and-plan path the web encoder drives.
renderVideo({required Video video, required Directory outDir, required ShellMount pumpWidget, required ShellFramePump pumpFrame, required SetViewSize setViewSize, ShellRunAsync runAsync = runAsyncDirectly, String compositionKey = 'render', int? frameCountOverride, bool cacheEnabled = false, Directory? cacheRoot, Aspect? aspect, Quality? quality, Export? export, Time? posterTime, MediaResolver? resolver, SnapshotService? snapshotService, BeatDetectionService? beatDetector, FrequencyAnalyzer? analyzer, String? defaultFontFamily, FrameCaptureService capture = const RepaintBoundaryCaptureService(), void onProgress(int completed, int total)?, void onCacheReport(int completed, int total)?}) Future<RenderManifest>
Captures video into outDir (frames.rgba + manifest.json, manifest written last) — the one capture path every Fluvie renderer drives.
resolveAudioMix({required Video video, required int fps, int totalFrames = 0, GenerativeResolver? generative}) ResolvedAudioMix
Resolves video's declared Audio tracks into an encoder-neutral ResolvedAudioMix for a non-FFmpeg encoder (such as fluvie_mobile_encoder).
resolverScope(MediaResolver? injected, {NetworkAllowlist? networkAllowlist, WebClipDecoder? clipDecoder, int? clipDecodeMaxEdge, bool streamClipFrames = true}) ResolverScope
Resolves the MediaResolver a render uses and the matching teardown.
runAsyncDirectly<T>(Future<T> callback()) Future<T?>
The ShellRunAsync for a host that already has a real event loop: it simply awaits callback.
runGuarded(List<Future<void> Function()> cleanups, void onError(Object error, StackTrace stackTrace)) Future<void>
Runs each cleanup in cleanups in order, routing any thrown error to onError and never re-throwing.
runStage<T>(RenderPhase phase, Future<T> body()) Future<T>
Runs body, stamping phase onto any FluvieRenderException it throws (when not already stamped) so the failure records which render stage broke.
stageResolvedAudioToSandbox({required List<ResolvedAudioTrack> tracks, required RenderSandbox sandbox, required AudioByteLoader loadBytes, double masterVolume = 1}) Future<AudioMixPlan>
Stages every resolved audio tracks into sandbox and builds the encoder AudioMixPlan, without touching the file system.
writeRenderProgress(String path, int completed, int total) → void
Writes "<completed>/<total>" to path atomically, for a launcher polling a render's progress.

Typedefs

AudioByteLoader = Future<Uint8List> Function(String source)
Loads the bytes of an audio source string (an asset key, file path, or URL).
AudioMixLanes = ({FfmpegAudioMix? amix, List<FfmpegAudioNode> nodes})
The encoder audio lanes a render contributes: the per-track FfmpegAudioNodes and the FfmpegAudioMix that combines them (null mix = no audio).
AudioMixStager = Future<AudioMixLanes> Function({required MediaResolver resolver, required Directory sandbox})
Stages the pre-resolved audio into the render sandbox and returns its encoder lanes. Injected so rendering stays independent of the audio feature layer: the shell wires the audio layer's stageAudioMix, tests pass a fake.
ClipMetadata = ({double fps, int frameCount, bool hasAudio, int height, int width})
The probed facts a Clip needs to resample a source video deterministically — its frame rate, frame count, pixel dimensions, and whether it carries an audio track (so the encoder mixes a clip's embedded audio only when present).
FrameEncoder = Future<Uint8List> Function(Uint8List rgba, int width, int height)
Compresses one captured frame's raw RGBA pixels to encoder-ready bytes.
FramePump = Future<void> Function(int frame)
Pumps the tree to frame n and returns once that frame is fully built — the host (a widget test, the capture harness, the off-screen web/mobile driver) owns the pumping mechanics.
GeneratedAssetMeta = ({Duration? duration, bool hasAudio, int? height, int? width})
The probed facts about a produced generative asset the render needs: its duration (audio/video), whether a video carries an embedded audio track (hasAudio), and its pixel size when known.
GenerativeProgress = ({int index, String providerId, String stage, int total})
Coarse progress for one generative asset during the prerender pass, surfaced to the render's progress callback: which asset (index of total), its providerId, and a human stage (for example generating, downloading, cached).
ProgressCallback = void Function(int completed, int total)
Reports capture progress: completed of total frames are written.
RenderAspectResult = ({RenderConfig config, RenderManifest manifest})
What render returns: the captured manifest and the config whose width and height were re-derived from Aspect.sizeFor for the rendered aspect.
RenderProgressCallback = void Function(RenderProgress progress)
A sink for RenderProgress updates, passed to a renderer's render(...).
ResolvedMedia = ({Uint8List bytes, String contentHash})
One pre-resolved media asset: its raw bytes and the contentHash that keys caches and render digests.
ResolverScope = ({Future<void> Function() dispose, MediaResolver resolver})
A media resolver for one render plus the teardown that releases it.
SandboxFramePump = Future<void> Function()
Pumps the host one frame after the controller seeks.
SandboxMount = Future<void> Function(Widget tree)
Mounts a capture shell (Widget) into the host before the frame loop.
SetViewSize = void Function(int width, int height)
Points the host's view at the render canvas.
ShellFramePump = Future<void> Function()
Pumps the host one frame after the controller seeks, so the just-seeked frame is fully built before its pixels are read. A widget test passes () => tester.pump(); the CLI passes its binding's pump.
ShellMount = Future<void> Function(Widget tree)
Mounts tree (the full capture shell) into the host's element tree before the frame loop starts. A widget test passes tester.pumpWidget; the CLI passes its binding's pump.
ShellRunAsync = Future<T?> Function<T>(Future<T> callback())
Runs callback on a real event loop, returning its result.