Skip to content

iOS parity, structural orientation/mirror, frame-lifetime safety, honest packaging & docs - #2

Merged
JenteJan merged 16 commits into
mainfrom
ios_support
Jun 27, 2026
Merged

iOS parity, structural orientation/mirror, frame-lifetime safety, honest packaging & docs#2
JenteJan merged 16 commits into
mainfrom
ios_support

Conversation

@JenteJan

Copy link
Copy Markdown
Owner

Brings the plugin to real iOS+Android parity and a credible, adoptable state. Device-verified on Pixel 8 (Android) and iPhone SE / iOS 18 (preview, photo, video recording, scanning, orientation, mirror).

Highlights

  • iOS: AVAssetWriter video recording, setFocusDistance, barcode symbology filtering, teardown/lifecycle/buffer fixes, deterministic orientation.
  • Android: all-plane YUV + strides over FFI, audio-gated recording, mic-permission await, dynamic orientation, tap-to-focus, mirror.
  • Structural orientation fix: read the framework's rotation (CameraX TransformationInfo / iOS sensor-relative) and apply it once in CameraPreview — fixes the recurring 90° tilt; adds previewRotation/displayPreviewSize.
  • Single mirror flag drives both the front preview and the saved photo/video.
  • Frame processor: dropped the no-op relay isolate (honest main-isolate docs); exposed per-plane strides for real CV.
  • Adversarial review: 21 latent bugs (frame-lifetime UAF, recorder/photo races, teardown hangs, recording robustness) fixed.
  • Adoption pass: compile-correct README quickstart + permissions/photo/video/lifecycle/native-C++ docs, expanded platform matrix, Limitations & Roadmap, example now demonstrates the FFI frame read (live brightness), tests 7→13.
  • Packaging: honest podspec/manifest/CHANGELOG, CI (format/analyze/test/dry-run/pana + native builds), PrivacyInfo.

Known residuals (documented)

  • Frame processor runs on the main isolate (off-isolate worklets are roadmap).
  • Narrow clearFrames use-after-free on framework activity/engine-detach (re-init/dispose paths fixed).
  • RecordVideoOptions, runtime setMirror/switchCamera, typed exceptions are roadmap.

- Modified `project.pbxproj` to include new Pods and framework references.
- Updated `contents.xcworkspacedata` to include Pods project.
- Enhanced `AppDelegate.swift` to support implicit Flutter engine initialization.
- Added scene configuration in `Info.plist` for better scene management.
- Updated `pubspec.lock` with new package versions and dependencies.
- Created a VSCode task to remove duplicate header files.
- Refactored `FlutterNativeVisionCameraPlugin` to forward to Swift implementation.
- Introduced `VisionCamera_FFI.h` for better C/C++ interoperability.
- Updated podspec to include C++ source files and improved header search paths.
- Enhanced `flutter_native_vision_camera.h` for better type definitions and structure.
- pubspec 0.0.4, drop unused plugin_platform_interface, add topics/issue_tracker, raise Flutter floor
- real podspec metadata + frameworks + PrivacyInfo.xcprivacy; remove deprecated AndroidManifest package attr; add RECORD_AUDIO
- replace broken native-build workflow with CI (format/analyze/test/dry-run/pana + per-platform native builds)
- honest README (real platform matrix, threading, orientation & mirror docs) and newest-first CHANGELOG
- frame processor callback runs directly via NativeCallable.listener (honest main-isolate semantics), instance-scoped, always decrements
- Android: pass all 3 planes with row/pixel strides; honor planeIndex; stride-aware luminance; working incrementRefCount
- atomic frame-processor callback pointer; iOS lock-once/CFRetain balance
…cus, mirror

- gate withAudioEnabled behind RECORD_AUDIO with video-only fallback; propagate recording errors
- requestMicrophonePermission actually awaits result code 1002
- report framework rotation (TransformationInfo) + front-preview mirror state to Dart
- dynamic capture orientation, correct tap-to-focus metering, real photo orientation, mirror setting
- fix pre-existing it.cameraInfo compile error
- implement start/stop/cancel recording (reuses the live frame stream), setFocusDistance, barcode symbology filtering + Vision orientation
- teardown-on-reinit, CVPixelBuffer retain + lock balance, honest pixel format, main-thread results
- deterministic preview rotation + report mirror state; mirror setting for photo/video
- fix pre-existing .externalUnknown / .codabar availability errors
- single source of truth: controller.previewRotation/displayPreviewSize/previewMirrored from native
- CameraPreview applies rotation once, mirrors only when needed (no double-mirror), BoxFit-aware tap-to-focus
- single 'mirror' flag drives both preview and captured photo/video; ResizeMode honored
- CameraPreview rebuilds reactively via ListenableBuilder
- C: free NativeFrame only on final decrement (so incrementRefCount/retain works); iOS retain takes a matching CVPixelBuffer lock; mutex serializes dispatch vs callback teardown
- controller: stop the frame pipeline + reset session state (active/recording/texture/size) before re-init/device-switch, preventing a use-after-free of recycled buffers
- CameraPreview: tap-to-focus inverts the actual rendered flip, not the desired mirror state
- nil the recorder on the session queue (not the asset-writer queue) to avoid racing the frame-append path
- complete a pending photo result on teardown so the Dart future never hangs on dispose/device-switch
- guard AVAssetWriter.startWriting() failure instead of appending into a non-writing writer
- report oriented (portrait) recorded dimensions
- on a Finalize error, drop the dangling recording (so a later stopRecording doesn't hang) and surface onError
- reject a concurrent microphone-permission request instead of overwriting/leaking the pending Dart result
…r in Android video

- iOS: all pendingPhotoResult access (set/read/clear) now on the session queue, reject overlapping captures
- Android: VideoCapture honors the mirror flag (MIRROR_MODE_ON_FRONT_ONLY) to match the photo path
- Frame.planeBytesPerRow(i)/planePixelStride(i) (C exports) so chroma planes can be walked correctly — the headline real-time-CV use case
- CameraDevices.getCameraFormat(device, targetWidth/Height/Fps) to pick a format without hand-rolling a comparator
- rewrite README: compile-correct quickstart with permissions, photo/video/scan/lifecycle snippets, correct native C++ plugin guide, requirements (iOS 13 / minSdk 21 / physical device), expanded platform matrix, Limitations & Roadmap
- correct the 'background isolate' claim in dartdoc to match reality (main isolate)
- document takeSnapshot (iOS-only), setExposure units, initialize pixelFormat/mirror; mark initializeNativeExamplePlugin demo-only
- rewrite the boilerplate example README
…el pages

- Native Vision Camera page reads the raw frame buffer over FFI and shows live average brightness (proves the headline feature, not just FPS)
- fix the C++ sample's row-stride indexing (was using width instead of bytesPerRow)
- relabel cards (Barcode/QR Scanner; FFI frame processor)
@JenteJan
JenteJan merged commit 91302fd into main Jun 27, 2026
6 checks passed
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