Skip to content

feat: stop-motion support (images → video)#149

Merged
hm21 merged 2 commits into
stablefrom
feat/stop-motion
Jun 19, 2026
Merged

feat: stop-motion support (images → video)#149
hm21 merged 2 commits into
stablefrom
feat/stop-motion

Conversation

@hm21

@hm21 hm21 commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Summary

Adds stop-motion support: render a video from a sequence of still images, each frame held for a configurable duration — the classic choppy stop-motion look. This was previously impossible since the whole render pipeline only accepted video sources.

A dedicated feature parallel to render (rather than touching the composition pipeline), so it reuses the existing task/progress/cancel infrastructure and is low-risk.

API

final data = StopMotionRenderData(
  frames: [
    StopMotionFrame(image: EditorLayerImage.file(File('frame1.png'))),
    StopMotionFrame(image: EditorLayerImage.memory(bytes),
        duration: const Duration(milliseconds: 500)), // per-frame override
  ],
  frameRate: 12,               // default frame duration = 1/12s
  fit: StopMotionFit.contain,  // contain | cover | stretch
  // resolution: Size(1080, 1920), // optional; defaults to the first frame size
);

final bytes = await ProVideoEditor.instance.renderStopMotion(data);
// or, memory-friendly for long sequences:
await ProVideoEditor.instance.renderStopMotionToFile(outputPath, data);

The output is silent — add music by passing the result through renderVideo with audioTracks.

Implementation

  • Dart: StopMotionRenderData / StopMotionFrame / StopMotionFit; renderStopMotion / renderStopMotionToFile on the platform interface, method channel and web (unsupported) stub.
  • Darwin (iOS/macOS): AVAssetWriter + pixel-buffer adaptor; EXIF-aware ImageIO decoding (CGImageSourceCreateThumbnailWithTransform) with downscaling.
  • Android: Media3 Transformer image input (setImageDurationMs) with a Presentation effect for the fit mode; ExifInterface orientation handling and inSampleSize downscaled decoding.
  • Frame preparation owns the first 20% of the progress bar so it no longer sits at 0% while decoding many photos.
  • Reuses RenderTask / RenderJobHandlecancel(id) works unchanged.

Platform support

Android iOS macOS Windows Linux Web
🚫

Tests & verification

  • flutter analyze (package + example): clean.
  • flutter test: all unit tests pass (model + method-channel coverage).
  • New integration test (example/integration_test/stop_motion_test.dart): duration, resolution, per-frame duration, all fit modes, to-file, progress, cancel.
  • Native compiles verified: flutter build macos and flutter build apk both succeed.
  • Example app gains a Stop-Motion page (synthetic sample frames out-of-the-box or pick images).

Version bumped to 1.20.0.

Render a video from a sequence of still images, each frame held for a
configurable duration — the classic stop-motion look.

- Dart: StopMotionRenderData / StopMotionFrame / StopMotionFit + new
  renderStopMotion / renderStopMotionToFile platform interface methods,
  method-channel and web (unsupported) implementations.
- Darwin (iOS/macOS): AVAssetWriter + pixel-buffer adaptor encoder,
  EXIF-aware ImageIO decoding with downscaling.
- Android: Media3 Transformer image input with a Presentation effect for
  the fit mode, EXIF orientation handling and downscaled decoding.
- Reuses the existing task/progress/cancel infrastructure; output is
  silent (add audio via renderVideo with audioTracks).
- Example app page, unit + method-channel + integration tests, README,
  CHANGELOG and version bump to 1.20.0.
@hm21 hm21 force-pushed the feat/stop-motion branch from 3a884ee to f8f9af9 Compare June 19, 2026 11:17
@hm21 hm21 merged commit 6764909 into stable Jun 19, 2026
1 check passed
@hm21 hm21 deleted the feat/stop-motion branch June 19, 2026 11:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant