flutter_handbreak 1.0.5 copy "flutter_handbreak: ^1.0.5" to clipboard
flutter_handbreak: ^1.0.5 copied to clipboard

Lightweight Flutter video & image compression inspired by HandBrake — quality-first, hardware-accelerated, any-format fallback. Easy one-liner API with presets & progress.

Changelog #

1.0.5 — 2026-08-31 #

  • Docs: consolidate changelog for published 1.0.4 (was split across 1.0.4–1.0.22 dev tags in git; pub.dev tarball now matches GitHub).

1.0.4 — 2026-08-31 #

Comprehensive update since 1.0.3 — published as 1.0.4 on pub.dev. This single release consolidates all fixes that were versioned as 1.0.4–1.0.22 during development:

  • Real-world input hardening (original 1.0.4): Android KEY_ROTATION fallback via MediaMetadataRetriever; truncated-input guard (last PTS vs duration); CQ KEY_QUALITY handling with VBR fallback; iOS progress-stream lifecycle fixes; podspec renamed to flutter_handbreak.podspec.
  • Channel map cast fix: CompressionResult.fromMap Map<Object?,Object?> strict cast → Map<String,dynamic>.from.
  • Example app: standalone Android/iOS hosts, camera-roll pickers, bundled fixtures, real widget test; unique <name>_<preset>_<timestamp> outputs in Documents (no OutputCreationException); findable results (Files app + Open/Copy + quality summary); image picker button; open_filex integration; Documents dir; result card for images.
  • Android encode robustness: 3-tier fallback (CQ+surface → VBR+ByteBuffer → forced software c2.android/OMX.google, AV1→H.264 when needed); YUV conversion hardening (base-offset aware, NV12/NV21/planar, 10-bit→8-bit); negotiated-format aware planar vs semi-planar; frame scaling (swscale stand-in); storage-dims fixes for rotation; bitrate-aware.
  • iOS pipeline: explicit AVAssetReader+AVAssetWriter with plan bitrate/quality (replacing export-session presets that ignored it); HEVC/H.264 explicit profiles; rotation as track metadata; storage-dims parity with Android; fail-fast status checks and two-phase watchdog; concurrent per-track requestMediaDataWhenReady interleaving (fixing 20% and 9.7% deadlocks); decode-to-raw before encode.
  • Preserve-resolution mode: VideoCompressionOptions.preserveResolution and copyWith(clear) support; example toggle (default ON) keeps original W×H; image keep HEIC + quality 62 for real savings vs HEIC source.
  • Diagnostics: all native error paths now report ExceptionClass: message @ File.method:line instead of Unknown error.

Full development history is preserved in git (1.0.4 → 1.0.22 tags/commits); this changelog is intentionally squashed for the published 1.0.4.

1.0.3 — 2026-08-23 (deep multimedia correctness) #

  • 🔴 muxer ordering: audio-transcode track registration counted in pendingTracks — MediaMuxer.start() now waits for BOTH video and audio track registration (previously a race where the video encoder's format-changed could start() before the audio track existed → addTrack after start() threw IllegalStateException on transcode jobs).
  • 🔴 bounded waits: iOS export wait is now watchdog-bounded — 30 s without session progress cancels the export and fails with TIMEOUT instead of hanging forever (done.wait() was unbounded).
  • 🔴 explicit queue capacity: JobManager now uses a structural ThreadPoolExecutor(1, ArrayBlockingQueue(8)) — no semaphore, no soft counters; overflow is rejected synchronously (QUEUE_FULL).
  • 🔴 cancellation semantics: CANCELLING (requested) is distinct from CANCELLED (terminal). cancelJob requests; the worker drains, then the plugin marks the terminal state. iOS mirrors this with a cancelRequested flag + .cancelling state.
  • Integration/stress lane: integration_test/ device harness (probe, rotation, A/V sync, keep-original, cancellation storms, queue bounds, decompression-bomb) with fixture manifest — ready to run on hardware, documented as UNVERIFIED until run.
  • Claims tightened: README image/filter claims match implementation; install example pinned to ^1.0.2; archive size updated to measured ~102 KB / zero .so; explicit "Verification status" section added.
  • tool/verify.sh extended with the iOS Swift typecheck lane.
  • JVM test suite: 16/16 passing (bounded-queue determinism fixed).

1.0.2 — 2026-08-23 (first real Kotlin compile — critical) #

  • The Android plugin now actually compiles. First standalone Gradle build (unit tests + release AAR) surfaced genuine compile errors that a consumer's app build would have hit:
    • Options.container property missing (unresolved reference)
    • Plan.containerFallbackNote/hwFallbackNote referenced but never parsed
    • MUXER_OUTPUT_THREE_GPP absent from compileSdk 34 android.jar → version-guarded literal
    • nullable result values vs Map<String, Any> → relaxed to Map<String, Any?>
    • missing return in transcode terminal statement
    • kotlin.math.abs import missing
    • mainHandler.post Boolean/Unit mismatch in waitForResult
  • 16 JVM unit tests now execute (Gradle testDebugUnitTest): Downmix, ResolutionHelper, JobManager (non-blocking submit, idempotent cancel, bounded queue, queued-job cancellation, state machine). All pass.
  • assembleRelease verified: 92 KB AAR, classes.jar only, zero native .so binaries (lightweight claim now measured, not assumed).
  • JVM test expectations corrected (stereo→mono output size, 5ch→stereo frame math, ExecutionException-wrapped task cancellation).

1.0.1 — 2026-08-23 (final hardening pass) #

  • P0: unbounded codec feed loops eliminated — NV12/PCM/EOS delivery is now bounded (15 s stall deadline), cancellation-aware, and fails with a dedicated stall error instead of hanging forever on a broken encoder.
  • P1: per-lane stall watchdog (audio activity can no longer mask a dead video lane); Android disposeJob now cancels running work (parity with iOS); iOS orientation applied exactly once (track transform zeroed when a videoComposition owns the transform — fixes double-rotation risk on resize/fps-cap); waitForResult moved to its own executor so probing a new file never blocks behind a running job.
  • P2: bounded admission queue (max 8 queued, QUEUE_FULL error); 100 MP decompression-bomb guard for images (fail safely, instruct caller); overwrite policy enforced on the extension-renamed image output; codec wait timeout 10 s → 1 s (fast cancellation); fractional FPS rounded.
  • P3: CompressionTimeoutException added to the typed error hierarchy (native TIMEOUT); docs updated with device-UNVERIFIED items.
  • Tests: +2 JVM queue tests (bounded admission, queued-job cancellation), +1 Dart error-parity case. 94 Dart + 17 JVM cases.

1.0.0 — 2026-08-23 #

  • First stable release (published on pub.dev as flutter_handbreak).
  • Documentation restructure: clean README + detailed docs under doc/ (API reference, quality model, platform matrix, architecture, migration roadmap).
  • Full validation: analyze clean, 94 unit tests green, Swift typecheck clean, pub.dev dry-run 0 warnings.

0.3.1 — 2026-08-23 (audit v3: crash & concurrency hardening) #

  • Android: semaphore no longer acquired on the main thread (ANR fix) — queued jobs wait on the worker.
  • Android: probe + capabilities moved off the platform thread; engine detach now cancels running jobs.
  • Android: audio transcode never drops decoded PCM (retry-until-fed, cancellation-aware).
  • Android: negative/non-monotonic PTS normalized before mux (MediaMuxer rejection fix).
  • Android: half-created decoder released on configure failure (no leak).
  • Android: job-scoped temp files (output.hbtmp.<jobId>) — no cross-job collision.
  • Android/iOS: requested HEIC/AVIF that a device cannot encode now falls back to JPEG and reports it (qualityWarning) instead of silently lying about the codec.
  • iOS: image decode no longer loads the entire file into memory (URL-based ImageIO source).
  • iOS: job task access synchronized (TSAN race fixed); duration validation parity with Android.
  • iOS: capabilities probe moved off the main thread.
  • Formal job state machine on both natives; state surfaced in progress payloads.
  • Dart: probe + capabilities resolved in parallel.

0.3.0 — 2026-08-23 #

  • Rename package to flutter_handbreak (keep handbreak.dart alias for compatibility)
  • Add lightweight FlutterHandbreak facade: compressVideo(path, quality: 80, preset: ...) one-liner
  • HandBrake-inspired credit banner + funding metadata for pub.dev
  • Exclude internal docs (PRODUCTION_REVIEW) from published archive — clean open-source
  • Fix facade unused import, example dependency rename

0.2.0 — 2026-08-22 (production hardening) #

Full audit of v0.2 hardening (internal, not shipped).

Architecture #

  • EncodePlanResolver: all encode policy (dimensions, fps gate, container fallback, rate control, hardware decision, audio plan, filter ordering) resolved once in pure Dart with 30+ unit tests; natives execute the plan instead of re-deriving heuristics.

Fixed #

  • Android: audio was dropped entirely → now passthrough or AAC-LC transcode, PTS-interleaved.
  • Android: framerate gate corrupted bitstream (empty-buffer queue) → decoder-output PTS gate, drop-only, deterministic.
  • Android: byte-buffer fallback was a silent hang stub → real YUV_420_888→NV12 converter (semi-planar + planar strides) feeding encoder.
  • iOS: Swift would not compile (inout-capture handler, missing UIKit import) → clean rewrite across Support/HardwareProbe/MediaProbe/JobManager/pipelines; typechecks on iOS SDK.
  • iOS: EXIF orientation computed but ignored → transform-aware thumbnail decode (all 8 cases).
  • iOS: fake usedHardwareAcceleration → real VTCompressionSession hardware-required probes; unenforceable softwareOnly recorded honestly in result notes.
  • MKV/WebM requests that mobile muxers cannot write now fall back to MP4 with an explicit note in qualityWarning instead of failing at mux time.
  • Dart: ImageCompressor.compress typed result; platform registration via ensureInitialized() (no private-class sniffing); progress streams self-terminate (no leak).
  • Hardware-decode capability no longer counts non-video decoders.

Added #

  • Container/audio/hardware fallback matrix with surfaced notes (ResolvedPlan).
  • Stall watchdog on Android pipeline (fails loudly after 30 s without progress).
  • Tests: resolver fallbacks, filter canonical order, validation helpers, error-code parity.

0.1.0 — 2026-08-22 #

  • Initial release, Phase 1.
  • Dart API: VideoCompressor / ImageCompressor, HandbreakProbe, presets, quality mapper, hardware detection abstraction.
  • Android: MediaExtractor probe, MediaCodec encode (H.264), MediaMuxer, Bitmap image path, JobManager with cancellation & temp cleanup, progress via EventChannel.
  • iOS: AVFoundation/VideoToolbox pipeline, ImageIO/CoreGraphics image path, JobManager.
  • Docs: ARCHITECTURE.md (HandBrake analysis), THIRD_PARTY_LICENSES, example app, benchmark harness.
0
likes
150
points
147
downloads

Documentation

API reference

Publisher

verified publisherrahulsha.com.np

Weekly Downloads

Lightweight Flutter video & image compression inspired by HandBrake — quality-first, hardware-accelerated, any-format fallback. Easy one-liner API with presets & progress.

Repository (GitHub)
View/report issues

Topics

#video #image #compression #media

Funding

Consider supporting this project:

github.com

License

MIT (license)

Dependencies

flutter, meta, plugin_platform_interface

More

Packages that depend on flutter_handbreak

Packages that implement flutter_handbreak