CameraView
Shiny.Maui.Controls.Camera is a cross-platform CameraView for .NET MAUI — live preview with zoom, torch, lens selection, photo & video capture, and live color filters — backed by AVFoundation (iOS / Mac Catalyst / macOS AppKit), CameraX (Android) and Media Capture (Windows). A matching Shiny.Blazor.Controls.Camera brings the same control to Blazor WebAssembly via getUserMedia.
What sets it apart from other camera controls is the pluggable frame-analysis pipeline: drop one or more analyzers onto the view and it streams frames to them off the UI thread. Each analyzer raises its own strongly-typed event (or a bindable Command), can be declared right in XAML, and returns styled bounding boxes drawn by the built-in CameraOverlayView. Ships with analyzers for barcode/QR scanning, face detection, motion detection, OCR, and structured documents — invoices (with order lines), AAMVA driver’s licenses, health cards, credit cards, and passports (MRZ). Each document is a strong record with nullable fields.
Features
Section titled “Features”- Live preview with
AspectFill/AspectFitscaling. - Lens & device selection —
Facing(Back / Front) or pick an exact device (multiple back lenses, USB webcams on macOS) viaGetAvailableCamerasAsync()+CameraId. - Zoom (clamped to the device’s reported range) and torch / flashlight.
- Photo capture → JPEG bytes, and video recording with optional audio.
- Live color filters — Mono, Noir, Sepia, Invert, Vivid, Cool, Warm, Fade, Chrome, Instant, Tonal (Apple Core Image, Android RenderEffect, Blazor CSS).
- Frame-analysis pipeline — pluggable
IFrameAnalyzers with per-analyzer back-pressure (drop-on-busy); each surfaces its result via its own typed event/Commandand draws styled boxes via the built-inCameraOverlayView. See Frame Analyzers. - Modular analyzer packages — add only what you need:
.Camera.Barcode,.Camera.Face,.Camera.Motion,.Camera.Ocr,.Camera.Documents.
AI Skill
Section titled “AI Skill”Step 1 — Add the marketplace:
claude plugin marketplace add shinyorg/skills Step 2 — Install plugins:
claude plugin install shiny-client@shiny claude plugin install shiny-maui@shiny claude plugin install controls@shiny claude plugin install shiny-mediator@shiny claude plugin install shiny-data@shiny claude plugin install shiny-aspire@shiny claude plugin install shiny-extensions@shiny Step 1 — Add the marketplace:
copilot plugin marketplace add https://github.com/shinyorg/skills Step 2 — Install plugins:
copilot plugin install shiny-client@shiny copilot plugin install shiny-maui@shiny copilot plugin install controls@shiny copilot plugin install shiny-mediator@shiny copilot plugin install shiny-data@shiny copilot plugin install shiny-aspire@shiny copilot plugin install shiny-extensions@shiny Installation
Section titled “Installation”.NET MAUI
Section titled “.NET MAUI”dotnet add package Shiny.Maui.Controls.CameraRegister the handler alongside UseShinyControls():
builder .UseShinyControls() .UseShinyCamera();xmlns:cam="http://shiny.net/maui/camera"Add the platform permissions your app needs:
- iOS / Mac Catalyst / macOS —
NSCameraUsageDescription(andNSMicrophoneUsageDescriptionfor video with audio) inInfo.plist; the macOS camera entitlement when sandboxed. - Android —
<uses-permission android:name="android.permission.CAMERA" />(andRECORD_AUDIOfor video with audio). Minimum SDK 23 (CameraX requirement). - Windows — the
webcam(andmicrophone) capability inPackage.appxmanifest.
Blazor
Section titled “Blazor”dotnet add package Shiny.Blazor.Controls.Camera@using Shiny.Blazor.Controls.Camera@using Shiny.Controls.CameraSee Blazor Usage for the WebAssembly specifics.
Quick Start — MAUI
Section titled “Quick Start — MAUI”<cam:CameraView x:Name="Camera" Facing="Back" ScaleMode="AspectFill" Zoom="1" IsTorchOn="False" Filter="None" />protected override async void OnAppearing(){ base.OnAppearing(); if (await this.Camera.RequestPermissionAsync()) await this.Camera.StartAsync();}
protected override async void OnDisappearing(){ base.OnDisappearing(); await this.Camera.StopAsync();}
// Capture a stillvar photo = await this.Camera.CapturePhotoAsync();await File.WriteAllBytesAsync(path, photo.Data);
// Flip the lensthis.Camera.Facing = this.Camera.Facing == CameraFacing.Back ? CameraFacing.Front : CameraFacing.Back;Video Recording
Section titled “Video Recording”VideoRecordingOptions.IncludeAudio defaults to true; the audio permission is requested only when audio is requested.
await this.Camera.StartVideoRecordingAsync(new VideoRecordingOptions { IncludeAudio = true });// ... later ...var video = await this.Camera.StopVideoRecordingAsync(); // CameraVideo { FilePath, Duration }On Android, CameraX allows a limited number of concurrent use-cases, so video recording and the analysis pipeline are mutually exclusive —
StartVideoRecordingAsyncthrows a clear error while analyzers are attached. ClearCameraView.Analyzersto record.
Live Filters
Section titled “Live Filters”<cam:CameraView Filter="Noir" />Filter accepts None, Mono, Noir, Sepia, Invert, Vivid, Cool, Warm, Fade, Chrome, Instant, Tonal. Filtering is applied to the live preview (Apple CIFilter — the Fade/Chrome/Instant/Tonal set maps to the Core Image CIPhotoEffect* filters; Android RenderEffect color matrix on API 31+; Blazor CSS); on Windows it is best-effort.
Selecting a Camera
Section titled “Selecting a Camera”Facing picks by position, but you can enumerate every physical camera and pin an exact one — essential for phones with multiple back lenses or desktops with several webcams:
var cameras = await this.Camera.GetAvailableCamerasAsync();// CameraInfo { Id, Name, Facing, IsDefault }this.Camera.CameraId = cameras.First(c => c.Name.Contains("USB")).Id;Set CameraId back to null to fall back to Facing.
Properties
Section titled “Properties”| Property | Type | Default | Description |
|---|---|---|---|
Facing | CameraFacing | Back | Back / Front / External lens position |
CameraId | string? | null | Exact device id from GetAvailableCamerasAsync(); overrides Facing |
IsActive | bool | true | Whether the session is running |
Zoom | double | 1 | Zoom factor, clamped to MinZoom..MaxZoom |
MinZoom / MaxZoom | double | 1 | Supported zoom range (reported by the handler) |
IsTorchOn | bool | false | Continuous flashlight/torch |
FlashMode | CameraFlashMode | Off | Flash behaviour for still capture (Off/On/Auto) |
ScaleMode | PreviewScaleMode | AspectFill | How the preview fills the view |
Filter | CameraFilter | None | Live color filter |
ShowDetectionOverlay | bool | true | Whether overlay boxes are surfaced for the overlay |
Analyzers | IList<IFrameAnalyzer> | empty | Frame analyzers (also the XAML content property) — see Frame Analyzers |
IsRecording | bool (read-only) | false | Whether a recording is in progress |
Overlays | IReadOnlyList<OverlayBox> | empty | Latest aggregated overlay boxes (read-only) |
Methods & Events
Section titled “Methods & Events”| Member | Description |
|---|---|
RequestPermissionAsync() | Request camera permission |
StartAsync() / StopAsync() | Start / stop the session |
CapturePhotoAsync() | Capture a still → CameraPhoto |
StartVideoRecordingAsync() / StopVideoRecordingAsync() | Record video → CameraVideo |
GetAvailableCamerasAsync() | Enumerate cameras → IReadOnlyList<CameraInfo> |
MediaCaptured / VideoCaptured | Raised after a photo / video is captured |
OverlaysChanged | Raised with the latest aggregated overlay boxes (presentation only) |
CameraError | Raised on a camera or pipeline error |
For results, subscribe to each analyzer’s own typed event (
BarcodeDetected,FacesDetected,MotionChanged,TextRecognized,DocumentDetected) or bind itsCommand.OverlaysChangedis only the styled boxes for drawing.
When to Use What
Section titled “When to Use What”- Scan a barcode / QR → add a
BarcodeAnalyzer(Frame Analyzers). - Detect / box faces → add a
FaceAnalyzer. - Trigger on movement → add a
MotionAnalyzer. - Read raw text → add an
OcrAnalyzer(TextRecognized). - Parse an invoice / receipt (header + order lines) → add an
InvoiceAnalyzer. - Scan a driver’s license / health card → add a
DriversLicenseAnalyzer(deterministic AAMVA) orHealthCardAnalyzerfrom.Camera.Documents. - Scan a passport → add a
PassportAnalyzer(deterministic MRZ → number, names, nationality, DOB, expiry). - Read a credit card → add a
CreditCardAnalyzer(brand + number deterministic; name/expiry best-effort). - Just take a photo or record video → no analyzers required; use
CapturePhotoAsync/StartVideoRecordingAsync.