captureToDirectory method

Future<RenderManifest> captureToDirectory({
  1. required RenderConfig config,
  2. required Directory outDir,
  3. required FramePump pump,
  4. required GlobalKey<State<StatefulWidget>> boundaryKey,
  5. required String compositionKey,
  6. Iterable<MediaSource> mediaSources = const [],
  7. Iterable<AudioSource> audioSources = const [],
  8. AudioMixStager? stageAudio,
  9. Export? export,
  10. int? posterFrame,
  11. ProgressCallback? onProgress,
})

Captures config.frameCount frames into outDir/frames.rgba and writes outDir/manifest.json last, returning the parsed manifest.

Order of operations: media.preResolveAll(mediaSources) → frame loop (config.startFrame ascending; per frame: cache lookup by digest+index, else pump then capture under boundaryKey, append to the frames file, cache store) → manifest write. The manifest embeds the complete encode argument array from VideoEncoderService.planEncodeArgs.

Implementation

Future<RenderManifest> captureToDirectory({
  required RenderConfig config,
  required Directory outDir,
  required FramePump pump,
  required GlobalKey boundaryKey,
  required String compositionKey,
  Iterable<MediaSource> mediaSources = const [],
  Iterable<AudioSource> audioSources = const [],
  AudioMixStager? stageAudio,
  Export? export,
  int? posterFrame,
  ProgressCallback? onProgress,
}) async {
  await media.preResolveAll(mediaSources);
  await media.preResolveAudio(audioSources);
  final digest = renderDigest(
    config: config,
    compositionKey: compositionKey,
    fluvieVersion: fluvieRenderVersion,
  );
  final sandbox = FileRenderSandbox(outDir);
  await sandbox.create();
  final lanes = stageAudio == null
      ? const (nodes: <FfmpegAudioNode>[], amix: null)
      : await stageAudio(resolver: media, sandbox: outDir);
  final sink = sandbox.openFrames(VideoEncoderService.framesFileName);
  try {
    await runFrameCaptureLoop(
      config: config,
      digest: digest,
      pump: pump,
      boundaryKey: boundaryKey,
      sink: sink,
      capture: _capture,
      store: _cache == null ? null : FrameCacheStore(_cache),
      onProgress: onProgress,
    );
  } finally {
    await sink.close();
  }
  final manifest = RenderManifest(
    width: config.width,
    height: config.height,
    fps: config.fps,
    frameCount: config.frameCount,
    framesFileName: VideoEncoderService.framesFileName,
    outputFileName: encoder.outputNameFor(export),
    renderDigest: digest,
    ffmpegArgs: encoder.planEncodeArgs(
      config,
      audio: lanes.nodes,
      amix: lanes.amix,
      export: export,
    ),
    posterFileName: posterFrame == null ? null : VideoEncoderService.posterFileName,
    posterArgs: posterFrame == null
        ? null
        : encoder.planPosterArgs(config, posterFrame: posterFrame),
  );
  await sandbox.writeText('manifest.json', jsonEncode(manifest.toJson()));
  return manifest;
}