nitro_camera 0.0.3
nitro_camera: ^0.0.3 copied to clipboard
High-performance vision-camera-style plugin built on the Nitro FFI bridge — photo/video capture, code scanning, frame processing, and GPU filters with zero method-channel overhead.
0.0.3 - 2026-11-04 #
Added #
-
Separate frame-processor size (Android) —
frameWidth/frameHeightonCameraViewandinitialize(): a sharp preview over small analysis frames (vision-camera's frame-outputtargetResolution). -
Photo output opt-out (Android) —
photo: falseleaves the JPEG stream out of the session for preview + frames only; photo/snapshot calls then throwCaptureException('capture/photo-disabled'). -
Direct frame delivery —
CameraFrameProcessor.attachDirect(): the worker isolate subscribes to the native frame stream itself (no UI-isolate hop) and processes only the newest frame of each batch. ~7 % faster end-to-end in the 1000-QR throughput benchmark, which now matches react-native-vision-camera on the same phone (both at the 30 fps camera limit). -
Direct preview (Android) — a
PreviewMode.texturepreview can take the camera's buffers straight into the Flutter texture, with no GL copy: theNitraGLThreaddisappears and the app uses ~3 % less of a core (OnePlus CPH2447, 1000-QR benchmark: 47.2 % vs 50.3 % mean CPU, 274 vs 285 MB RSS, same throughput).CameraViewuses it for its default Texture mode;CameraController(directPreview: true)opts in elsewhere. Dart rotates, mirrors and cover-crops the raw buffer from the newNitroCamera.getPreviewTransform/CameraController.previewTransform. The session falls back to the GL renderer by itself when a shader, lens undistortion, a platform view or GL recording needs it (CameraEventType.previewTransformChanged). Only with an ImageReader-backed texture (handlesCropAndRotation() == false); otherwise GL as before.
Fixed #
-
iOS recording (found by the first iOS Patrol run, iPhone 12):
- A session teardown during a recording (lens switch, resolution change)
now finalises the clip and emits
stoppedwith its path, instead of cancelling it and deleting the file. stopRecording()after amaxDurationMs/maxFileSizeBytesauto-stop is a clean no-op (empty, non-finalised result, like Android) instead of throwingrecorder/finalize-failed.- Back-to-back recordings no longer fail with a spurious timeout: a stop's 10 s watchdog could fail a LATER stop that was still finalising.
setTargetOrientationnow applies to recordings (the clip's display rotation), not only to stills.startHighSpeedRecordingno longer crashes (uncaught NSException) on formats that can't encode H.264 — it falls back to a writable codec (HEVC) and reports the codec actually used.
- A session teardown during a recording (lens switch, resolution change)
now finalises the clip and emits
-
iOS preview: toggling video HDR, low-light boost, stabilization or the pixel format no longer flashes black frames (up to ~440 ms) into the preview, frame processors and recordings — the last good frame is held while the pipeline rebuilds. HDR / low-light no-op re-applies are skipped.
-
Android: a DNG request on a camera that advertises RAW but never delivers a RAW frame (e.g. OnePlus logical multi-cameras) now throws
CaptureException('capture/raw-unavailable')instead of an untyped native error. Later DNG requests on that camera fail immediately (no second preview freeze), and the camera device is reopened — the HAL left it broken, which used to surface asERROR_CAMERA_DEVICEon the next operation. -
Android: the front camera's GL preview (platform view, shaders, lens undistortion) was not mirrored — the opposite of the stock selfie preview. It is now mirrored in screen space after rotation, like the direct preview. Recordings keep their existing (un-mirrored) output.
-
Scanner: small QR codes (2.5–4 px modules) now decode — a 2× upscaled centre pass joins the adaptive ladder.
-
CameraFrameProcessor: a handler that throws no longer silently kills the worker isolate (no more frames, no log). The frame counts as a miss, the first error is printed, and processing continues.
Changed #
-
Android startup: the pre-wired recorder surface (MediaCodec persistent surface + dormant MediaRecorder, ~80 ms) is now built while the camera hardware opens instead of after it. Cold start on a OnePlus CPH2447: camera opened → streams created 194 → 154 ms, first camera frames 772 → 720 ms (medians, 16 vs 14 launches); instant recording start is unchanged.
-
Upgraded to nitro 0.8.5 (weak Dart DL symbols — fixes the iOS "duplicate symbol" link error with two nitro SPM plugins, nitro_ecosystem#61).
-
NitroCamera.openCameratakes a trailingdirectPreviewargument (the raw native API;CameraControllerpasses it for you). -
Android: frames are posted to Dart from the camera thread (a direct hot flow) instead of hopping through a
Dispatchers.Defaultcollector: native → Dart handler p50 1.3 → 0.5 ms, p90 3.6 → 1.6 ms. With no buffer left to overflow, Android no longer emitsframeDroppedfor a slow Dart consumer; the Dart side coalesces a busy port instead. -
attachDirectframes are copied into one reusedNitroBuffer, so a@zeroCopynative call (e.g. nitro_zxing'sdecode) takes them by pointer with no second copy.FrameData.bytesis borrowed in this mode: valid only during the handler call. -
resolvedConfig/onConfigResolvedare now populated when the camera is opened from a plainwidth/height/fpsrequest (no format): the values come from the native session read-back, so a requested 60 fps that the hardware caps at 30 is reported as 30. Breaking:ResolvedCameraConfig.formatis now nullable (null in that case).
0.0.2 #
Added #
- High-speed (slow-motion) recording —
startHighSpeedRecording(path, fps)/stopHighSpeedRecording(): 120/240 fps → 8× slow-mo. - Native detector plugin registry —
NitraDetectors.register(name, factory); select custom detectors from Dart by name.barcode/facebuilt-in. - Manual focus & exposure —
setFocusDistance,setManualExposure,setAutoExposure(clamped natively). - Keep-screen-on —
setKeepScreenOn(bool)(FFI, no extra plugin). - Frame helpers —
FrameData.resized / cropped / rotated. - Scanner ROI —
CodeScanner(windowFraction: …). - Multi-cam discovery — typed
ConcurrentCameraSet+getConcurrentCameraSets.
Fixed #
- Android: recorded video no longer squished — encoder size matches session aspect.
- Android: fixed a persistent-surface teardown race that could crash the camera.
- iOS: back↔front switch with manual white balance no longer crashes.
- RAW/DNG on HALs that gate it now fails fast + falls back to JPEG (no freeze).
- Android:
RecordingResult.durationMswas inflated by the encoder's finalize latency (a 2 s clip could report 4.7 s). It now reads the finalized container's own duration, with a stop-requested wall clock as fallback. - Videos no longer save sideways — recordings ignored the requested orientation and were stored in sensor orientation.
isRecordingno longer gets stuck on when a recording ends by itself (duration/file-size limit or encoder failure) — Dart is now notified.- Encoder crashes are no longer silent — a dying recorder stops the
recording, deletes the unusable file and emits an
errorevent. - Photos no longer save 90° off in landscape, on both platforms;
PhotoResult.orientation/isMirrorednow describe the saved file accurately. - A failed stop no longer returns a truncated, unplayable file as success — it
throws
RecorderException('recorder/finalize-failed'). AddsRecordingFinishedReason.failedandRecordingResult.isFinalized. initialize()without a size/format now targets 1080p instead of the platform's first format (4K on Android, smallest on iOS).- iOS: an unsupported
setFrameFormatvalue no longer crashes the app. - iOS: external and Continuity Cameras (iOS 17+) now appear in
getAvailableCameraDevices(). - Android: opening external/UVC cameras that report no preview sizes no longer crashes; they're also labelled as external instead of phone lenses.
- Android: session state no longer reports
bgrawhen frames are delivered asyuv420.
Changed #
- Upgraded to nitro 0.7.5.
0.0.1 - 2026-07-12 #
Initial release.
- Curated exports:
nitro_camera.dartexposes the high-level API only. The raw FFI layer moved topackage:nitro_camera/native.dartand the FPS HUD topackage:nitro_camera/debug.dart. - Typed device model: positions, lens types, hardware level, extensions, auto-focus system and stabilization modes are enums rather than ints.
- Typed errors: failures throw
CameraExceptionsubtypes (PermissionException,DeviceException,SessionException,CaptureException,RecorderException) with stabledomain/codestrings.
Fixed #
frameStreamdelivered frames from other open sessions; it is now scoped to its own session.PreviewMode.platformViewrendered nothing off-Android; it now falls back to the texture path.- Pinch-to-zoom clamped to a hardcoded 1–8× instead of the device's real range.
- Unknown values from a newer native layer are skipped instead of throwing a
RangeError.