Repository navigation
Platforms and troubleshooting
Task: set up permissions and the camera provider for each platform, know exactly what has been verified, and fix the failures you are most likely to meet.
Conditions: your app depends on yuv_camera, yuv_ffi and camera (see
Getting started).
| Platform | Setup |
|---|---|
| Android |
minSdk 26 or higher, because yuv_ffi requires API 26. The camera plugin requests the camera permission at run time. |
| iOS |
NSCameraUsageDescription with a short reason in ios/Runner/Info.plist. |
| macOS |
NSCameraUsageDescription in Info.plist, and the com.apple.security.device.camera entitlement in each entitlements file you ship (typically the debug and release ones). |
| Windows |
camera_desktop in your pubspec.yaml; nothing else. |
| Linux |
camera_desktop in your pubspec.yaml, and the GStreamer development libraries (libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev) installed before building. |
| Web | Chrome over localhost or HTTPS, and the browser's camera permission. |
The plugins' documentation is the primary source for permission details:
camera and camera_desktop.
camera talks to the platform through a provider package. For Android, iOS and Web the default provider comes with
camera; on Windows, macOS and Linux your app adds camera_desktop next to camera, and it is picked up as a federated
plugin. yuv_camera does not depend on camera_desktop and does not register it: the choice of provider is the
application's.
The camera package restricts CameraController.startImageStream to Android and iOS. yuv_camera therefore reads frames
from the platform interface (CameraPlatform.instance.onStreamedFrameAvailable) directly, which is how desktop frames
reach onFrame, YuvCameraFrameSource and the preview. You do not call startImageStream yourself.
On Windows, frames from every camera except a back one are mirrored and not rotated, as in the camera_desktop preview,
which reports every Windows camera as front. On macOS and Linux the package applies no rotation and no mirror.
-
yuv_cameraopens a second media stream on the controller's device, matched by its unique, non-empty camera label. Empty, missing or duplicate labels fall back to the camera direction (frontrequestsuser,backandexternalrequestenvironment). The controller'sResolutionPresetand optionalfpsare requested asidealconstraints, so the browser may adjust them. Device enumeration or stream-opening failures stop the source and reachonError. -
Browsers expose the camera only in a secure context: serve the app from
localhostor over HTTPS, and let the user grant the camera permission (MDN:getUserMedia). -
Frames require
MediaStreamTrackProcessororHTMLVideoElement.requestVideoFrameCallback. Where the processor is unavailable, the source reads canvas frames scheduled byrequestVideoFrameCallback. If neither path is available, the source stops all its tracks and reportsUnsupportedError: Camera frames require MediaStreamTrackProcessor or requestVideoFrameCallbackthroughonError. This prerequisite does not extend the verified browser support below. -
Web frames arrive as BGRA and are not rotated. Frames from every camera except a back one are mirrored, as in the
cameraplugin's own Web preview; a laptop webcam usually has no facing mode, is reported as external and is mirrored too. -
Native
yuv420frames with two planes are NV12 (Y+ interleavedUV); three planes are I420 (Y,U,V). One-planebgra8888remains packed. Native row strides are preserved; malformed visible layouts terminate the stream throughonError. YUV conversion uses BT.601 limited/video range; the package does not detect full-range data or select another color matrix. The iOS NV12 fixture models plugin bytes only; physical NV12 capture (raw frames, drawn preview and capture, front and back cameras) was verified on an iPhone 11 with iOS 18.7.8, without separate portrait, landscape or mirror NV12 runs.
Backend availability, a successful build and a confirmed physical camera are different things. The checks run directly on
the maintainer's machines with the scripts in tool/ci/ of the Git repository (the pub.dev archive does not include tool/;
the repository has no hosted CI), and their results are recorded
against exact commits. So far they have confirmed:
| Check | Result |
|---|---|
| The example's tests | VM coverage includes camera lifecycle, device-check, example navigation, ML Kit input conversion, shape crop, orchestrator and face-screen behavior. Exact test totals are recorded with each gate run. |
| Native capture integration target | Passed with the fake camera platform, no physical camera: on Windows (Release), a physical Pixel 3 (Android 12, arm64-v8a, debug and Release builds), Ubuntu 24.04 in a VM, macOS 15.6 (arm64) and the iOS 18.6 simulator. |
| iOS 18.6 simulator fixture targets | Capture, shape-crop and BGRA targets pass on fixtures; no camera is involved. |
| Orchestrator-cycles integration target | Passed with a physical camera: Windows 10 built-in webcam (Debug); Chrome (JavaScript build) on Windows with a physical webcam and no fake device; physical Pixel 3 (Android 12, arm64-v8a, Release build) with camera 0.12.1; the Integrated Webcam presented as a VirtualBox Webcams virtual device (/dev/video0, uvcvideo) to an Ubuntu 24.04.5 LTS x86_64 guest in VirtualBox 7.2.20; macOS 15.6 (arm64, Debug) built-in camera. |
| Shape-crop integration target | Passed on the real YUV backend of the platform, without a camera, on the same platforms, devices and modes as the orchestrator-cycles target. |
| Minimum Android dependency |
camera 0.11.0+2 / CameraX 0.6.30 produced inconsistent physical Pixel 3 preview results (three failed runs and one passing run) and is outside the package dependency range, now >=0.12.1 <0.13.0. Physical Pixel 3 preview is verified with camera 0.12.1. |
| Desktop real preview/capture smoke | Passed in Debug on Windows 10 and macOS 15.6 (arm64); Windows orchestrator cycles also passed in Debug. |
| macOS 15.6 preview check | Passed on the built-in camera: two previews, the second continuing after the first is removed, and final close. |
| Web camera source smoke | Passed in Chrome (JavaScript build) on Windows with a physical webcam. |
| Web device-choice target | Passed in Chrome (JavaScript build) on a fake media stream with two distinct fake video inputs. This is the only fake-stream result; no physical camera is claimed. |
| iPhone 11, iOS 18.7.8 | Orchestrator, camera-flow and BGRA integration targets pass, and the signed Release device check passes (portrait idle and heavy load, landscape, face portrait and landscape, mirror, capture). The operator confirmed two previews, the second continuing after the first is removed, and landscape capture in Shape crop. |
| iOS observation | The behavior documented on YuvCameraFrame: one iPhone, iOS 18.7, front camera, portrait and landscape. |
A build, a passing emulator or a fake-stream target does not prove that a physical camera works on that platform. Physical-camera claims remain limited to the named devices, modes and scenarios. For Linux, the release bundle built in Release; the real-camera cycles and preview/capture targets, plus the fake-camera capture target, ran in Profile because Flutter Driver does not support desktop Release integration runs. The Engineer visually checked the preview and capture in the VM GUI: colors and orientation were correct, with no bands. The guest used Flutter 3.44.9, its GNOME Wayland session and virtual VMSVGA display; an EGL diagnostic reported llvmpipe for that diagnostic context and does not establish which renderer Flutter used. This result applies to the named Ubuntu guest, VirtualBox Webcams camera path and GUI session. It does not establish bare-metal Linux, other distributions or CPU architectures, other camera models or formats, physical-GPU performance or FPS, hotplug/suspend behavior, or compatibility with every Wayland or graphics configuration.
Additional physical preview/capture and stop/dispose consumer scenarios passed with Flutter 3.44.9:
| Browser / device | Checked build and scope |
|---|---|
| Firefox 143 on Windows | JavaScript, external webcam. |
| Safari 18.6 on macOS 15.6.1 | JavaScript, External-camera fixture only; no front/back lens claim. |
| Chrome on a Pixel 3 | JavaScript over HTTPS; the scenario passed, but the operator observed low speed and no speed threshold was defined. |
| Safari on an iPhone 11 | JavaScript over HTTPS. |
These are bounded consumer observations, not a complete browser conformance or image-operation parity matrix.
Camera --wasm, Safari/Firefox WASM and other browser/device configurations remain outside the support claims.
The standard Chrome scenarios use the default Flutter asset layout. General public assetBase/CDN/CSP support is not
established by internal-loader experiments. Static CocoaPods camera runtime remains unverified; the FFI-only static
Firebase consumer does not establish camera capture support. Before each release, each required result is run or carried
with evidence that the behavior, configuration and dependencies it covers remain applicable.
Chrome 155 on Windows with Flutter 3.44.9, JavaScript/CanvasKit, can report a VideoFrame collected without an explicit
close during camera teardown/reopening. Physical start/capture/stop/reopen scenarios passed, and the observed camera
frames were closed. The cause and owner remain unknown; this does not prove the absence of a package defect or a leak,
and no fix is claimed. The observation is retained for this release.
The investigation and preserved evidence record eight diagnostic/control runs and their limits. Revisit it for a reproducible stall/capture failure, confirmed resource accumulation or a proven missed-close path; recurrence of the warning alone does not establish one.
- Symptom: your code shows "no camera" and there is no preview.
- Check: does the device or computer have a camera that other applications can see? On desktop, is
camera_desktopinpubspec.yaml, and was the app rebuilt after adding it? - Action: handle the empty list in your UI, as Getting started does, and connect a camera or add the missing provider.
- Symptom: the exception is caught by your code and you see your own message, or the picture never appears.
- Check: was the permission denied? Compare the setup table above for your platform: Android needs
minSdk26 and the run-time prompt, iOS and macOS needNSCameraUsageDescription(macOS also the entitlement), the browser needs the permission on a secure origin. Is another application using the camera? - Action: fix the setup, then create a new
CameraControllerand callinitialize()again. A controller that failed to initialize should be disposed, not reused.
- Symptom: text starting with
Camera error:where the preview was, andonStreamStoppedis called after the stream error. Both preview widgets call it only after a stream error, never during removal or controller replacement. - Check: read the message.
Bad state: CameraController should be initializedmeans the controller passed to the view was not initialized. Another message is a failure of the frame stream. - Action: pass an initialized controller. After a stream failure open the camera again with a new controller: the source
that failed has stopped and the view shows the error until it is rebuilt with a new controller.
With
YuvCameraOrchestrator, useYuvCameraLeaseBuilder's Retry action; it drains and disposes the failed controller before opening a replacement. Do not start another native image stream on a controller while a YUV source is active.
- Symptom: smooth preview until
onFrameortransformdoes real work. - Check: heavy synchronous work in
onFrameortransformruns on the UI isolate and stops painting. - Action: shorten the work, raise
onFrameInterval, or move the work to another isolate; see Frame processing.
- Symptom: the capture button does nothing, or your code reports no image.
- Check: is the view built and attached (you passed its
viewController)? Was the view or the controller replaced, or the stream stopped, before a frame was drawn? - Action: capture when the preview is visible, keep the page alive while waiting, and show a message for
null. Details: Preview and capture.
- Symptom: your callback runs a few times per second at most.
- Check:
onFrameIntervaldefaults to 200 ms, andonFramenever overlaps itself: while an async call is pending, frames are skipped. - Action: lower
onFrameInterval(Duration.zerooffers every frame) and make the callback return sooner; useYuvCameraFrameSourcefor your own pacing.
- Symptom: boxes are mirrored, rotated or shifted.
- Check: which coordinates are they in, and which matrix maps them?
- Action: map upright boxes with
geometry.uprightToView; see Geometry and overlays.
- Symptom: the build cannot find GStreamer headers or libraries.
- Check: are the GStreamer development packages installed?
- Action: install
libgstreamer1.0-devandlibgstreamer-plugins-base1.0-dev(or your distribution's equivalent), then build again.
- Symptom: in Chrome,
availableCameras()throws aCameraExceptionwith the codecameraNotReadablebefore any camera is chosen, so no preview starts.YuvCameraOrchestrator.acquire()ends with the same error, which it keeps as its error state instead of hiding it. - Check: is a virtual camera registered with the system but not running, for example OBS Virtual Camera while OBS is not
providing it? The
camera_webversions checked list every device and stop at the first one the browser cannot read. - Action: start the virtual camera (in OBS, Start Virtual Camera), or turn it off or unregister it so that only working
cameras remain, then reload the page.
yuv_cameradoes not change how the plugin lists or opens devices.
- Symptom: the example's face detection does nothing on Windows, macOS, Linux or Web.
- Check: the example runs ML Kit face detection only on Android and iOS and skips it elsewhere.
- Action: wire a detector that supports your platform, or run detection only on Android and iOS as the example does.
- Getting started: the first working preview.
- Frame processing: throttling and heavy work.
- yuv_ffi: Platforms and troubleshooting: initialization, native library and Web module errors.
The package allows Flutter 3.44.0 or later. Flutter 3.44.9 is the officially tested and supported version. Earlier Flutter 3.44.x patch releases may work, but have not been verified.
Web evidence includes Chrome JavaScript and the limited Firefox/Safari/mobile JavaScript consumer scenarios listed
above. It does not establish support for every browser or device. General public custom asset placement/CDN/strict-CSP
support and static CocoaPods camera runtime remain unverified. Camera --wasm is not claimed.
A desktop camera provider does not prove physical capture support. Physical iPhone NV12 front/back preview and capture evidence does not establish
separate NV12 landscape or mirror support.