react-brain
Browse decisions
React & language foundations 3App architecture 11UI 13Platform & native 9Build, test, observe, secure 6AI in React apps 3stack composerdecision recordscensusstaleness benchnewsletterarchitecturechangelogmethodologyroadmap

entriesplatform-native · verified 2026-09-24 · react + react-native

Camera, video & real-time media (WebRTC, frame processing, filters)

reviewedconfidence: lowtrack: weakenedthis tier holds 64% on the public scorecard (7/14 graded · 0 overturned) →

related decisions: native

cited by: uploads · ondevice-ai

re-verified 6× — 2026-09-24 · 2026-09-10 · 2026-09-01 · 2026-08-18 · 2026-07-13 · 2026-07-10 · changelog

recommendation

Camera → VisionCamera (frame processors) or expo-camera (managed). Video calling → LiveKit (open-source, self-hostable) or a hosted SDK (Daily/Agora/Stream), not raw react-native-webrtc. Real-time filters → process VisionCamera frames, GPU-composite, inject into the existing WebRTC source. Playback → expo-video/expo-audio; images → expo-image. Calling-SDK specifics are lightly vetted (confidence: low): prototype per-device.

  • custom real-time effects (blur / segmentation / virtual bg) → VisionCamera frame processors (+ Skia/Metal + MediaPipe/Vision, injected into the WebRTC VideoSource)
  • self-hosted / open-source calling → LiveKit (@livekit/react-native 3.x)
  • fastest managed calling setup → Daily / Agora / Stream (hosted SDK; pick on pricing and features, not yet verified here)
  • raw WebRTC with your own signaling/SFU → react-native-webrtc (M124 line, patch-only; only when no calling SDK fits)
  • just capture photos → expo-camera (or VisionCamera for advanced)
  • code scanning, no custom camera UI → platform scanner (one call; choose by the native engine, not the wrapper — see the barcode reading's engine map)
  • code scanning with overlays / validation → VisionCamera + barcode plugin
  • code scanning inside Expo Go → expo-camera
  • shipping camera to prod NOW → VisionCamera v5 (pinned patch + a device-matrix pass; still stabilizing, see note; don't linger on v4, frozen since 2025-11)
  • displaying images (caching, blurhash placeholders, transitions) → expo-image, not core <Image>
  • video/audio playback → expo-video / expo-audio (migrate off expo-av: deprecated, unpatched, out of Expo Go since SDK 55)
  • audio PROCESSING (graphs, effects, filters, real-time) → react-native-audio-api (Web Audio API; real-time discipline: rt-audio-pipeline-audit)
  • real-time pipeline correctness & perf → react-native-jsi skill (frame processors), then rt-audio-pipeline-audit (the real-time discipline transfers from audio)
  • native call UI (CallKit / Android telecom: incoming-call screen, hold/mute, VoIP push) on Expo → expo-callkit-telecom (0.5.x; Jetpack Core Telecom; needs expo-notifications; not react-native-callkeep, see note: CALL UI)
  • native call UI when your calling SDK ships its own call-UI + push integration → the calling SDK's integration (e.g. Fishjam's RN client)

Low confidence — fast-moving or lightly-vetted domain: treat the pick as a vetted lead and prototype before committing.

Options & tradeoffs

the field considered — and why each one isn’t the default here

optiontradeoffevidence
VisionCamera (v5, Nitro)RN camera with JSI frame processors, rebuilt on Nitro in v5; the base for custom real-time frame pipelines (RB-E-NATIVE owns Nitro). 5.2.3 (2026-08-20, verified vs npm); v4 frozen at 4.7.3 (2025-11-12). See note: VISIONCAMERA V5.655k/wk · ships in 7/34
expo-cameraManaged camera for Expo apps; simpler, fewer real-time hooks than VisionCamera; the scanner that works in Expo Go.2.9M/wk · ships in 5/34
react-native-webrtcLow-level WebRTC bindings; you wire signaling/SFU yourself. Patch-only on Chromium M124: 124.0.8 (2026-07-21, verified vs npm) is the fifth 124.0.x patch since 2024-08, while LiveKit's fork is on M144 — prefer a calling SDK. See note: WEBRTC LINES.303k/wk · ships in 2/34
LiveKit / Daily / Agora / Stream VideoWebRTC conferencing SDKs (RN + web); LiveKit is open-source + self-hostable, the others are hosted infra. @livekit/react-native 3.0.0 (2026-09-11, verified vs npm) moved to LiveKit-prefixed WebRTC builds; see note: WEBRTC LINES.
Skia / Metal + MediaPipe / Vision / ML KitGPU compositing (Skia/Metal) + on-device segmentation/detection (MediaPipe on Android, Vision on iOS) for filters/effects. See note: FILTER PIPELINE.
expo-video / expo-audioMedia PLAYBACK (not capture or calling), the replacements for expo-av, which is DEPRECATED (removed from Expo Go in SDK 55, no longer patched); expo-video splits player logic (VideoPlayer) from view (VideoView).2.5M/wk · ships in 3/34
expo-imageImage DISPLAY, the modern replacement for core <Image>: disk/memory caching, blurhash/thumbhash placeholders, transitions, faster decode; works in bare RN, not just Expo.5.8M/wk · ships in 3/34
react-native-audio-api (Software Mansion)Web Audio API for RN: audio graphs, effects/filters via MediaElementAudioSourceNode; the 0.13 line (2026-07, verified vs npm) ships a stable <Audio/> component. The AUDIO-processing layer; playback-only needs go to expo-audio.192k/wk

evidence: npm weekly downloads (signals snapshot) · “ships in n/D” = adoption across the production-app census, honest denominators

Migration lines

version and deprecation lines this decision tracks — for YOUR repo's sequenced plan: the doctor or npx -y @heart-it/react-brain migrate .

expo-av → expo-video (playback) + expo-audio [deprecated · effort M]

expo-av is DEPRECATED — removed from Expo Go in SDK 55 and no longer patched; expo-video splits player logic (VideoPlayer) from view (VideoView), expo-audio covers audio playback

expo.dev/changelog/sdk-55

npm weekly downloads (from the corpus's last signals run): react-native-vision-camera 655k · expo-camera 2.9M · react-native-webrtc 303k · expo-av 517k · expo-video 2.5M · react-native-audio-api 192k · react-native-qrcode-svg 1.3M · expo-image-picker 5.8M

Verified notes

THE PICTURE: real-time media in RN is built by composing, not forking. The durable lesson from the Margelo filters deep-dive: DON'T fork the WebRTC stack. Intercept camera frames, run segmentation/detection OFF the hot path, GPU-composite, and inject the result into the EXISTING WebRTC VideoSource (the source doesn't care who produces frames). The same stance runs through the rest of the entry: a calling SDK over raw WebRTC, VisionCamera frame processors over a custom camera, and platform scanners over wrappers. Options are lightly vetted (confidence: low); calling-SDK specifics (pricing, features) are not verified here, so prototype on-device. FILTER PIPELINE (verified vs the Margelo deep-dive): segmentation/detection runs off the hot path (~20-30ms, "latest mask wins" rather than blocking), MediaPipe on Android and Vision on iOS, composited with Metal/Skia; the gotchas are YUV↔I420/NV12 conversion, monotonic timestamps and buffer pooling. Encoder, transport and signaling stay untouched. WEBRTC LINES (2026-09-24, verified vs npm and the LiveKit 3.0.0 release): react-native-webrtc is patch-only on Chromium M124 (124.0.4 2024-08-14 through 124.0.8 2026-07-21). @livekit/react-native 3.0.0 (2026-09-11) peers LiveKit's own fork @livekit/react-native-webrtc ^144.2.0 and moves to LiveKit-prefixed WebRTC builds (io.github.webrtc-sdk:android-prefixed on Android, the LiveKitWebRTC pod on iOS) to prevent collisions with any other WebRTC implementation → an app mixing LiveKit with another WebRTC library should take 3.x; the maintained WebRTC line in RN today is LiveKit's. CALL UI (verified vs the Software Mansion callkeep-alternatives post, 2026-08-26, and npm): react-native-callkeep is dormant (4.3.16, 2024-11-18, still its latest) and built on the pre-Jetpack android.telecom.ConnectionService. A second, source-independent trap the dormancy hides: Android hands every FCM push to a single service per app, so the VoIP call push fights whatever notification library you already ship for that slot → the working alternatives parse VoIP pushes natively and RELAY other FCM messages to expo-notifications, RN Firebase or your own handler. expo-callkit-telecom 0.5.0 (2026-09-09, verified vs npm) uses Jetpack Core Telecom and peers expo, expo-notifications and @livekit/react-native-webrtc >=144.0.0, so it assumes LiveKit's WebRTC fork. Fishjam's RN client bundles call UI + push parsing; that is Software Mansion recommending its own platform, but the callkeep diagnosis is verified independently. VISIONCAMERA V5 (5.0.0 2026-04-16 and v4's last publish 4.7.3 2025-11-12, verified vs npm): v5 is still stabilizing: open iOS-26 teardown/race crashes + an AE regression vs v4, and ~85% of installs remain on v4 (2026-07) → adopt v5 pinned-patch with a device-matrix pass; v4 is frozen. EXPO-AV (verified vs the SDK 55 changelog): expo-av is deprecated, removed from Expo Go in SDK 55 and no longer patched → the migrate row sequences expo-video + expo-audio. HISTORY: 2026-09-24 REOPENED: react-native-webrtc now five M124 patches (was "2 releases since mid-2024"); LiveKit RN 3.0.0 prefixed WebRTC builds; expo-callkit-telecom 0.4.x → 0.5.0 (LiveKit WebRTC peer). 2026-09-10 GAP CLOSED: native-call-UI clause gains the Android FCM one-service-slot collision, reconciled from harvest/2026-08-27. 2026-06-25 GAP CLOSED: entry created from the Margelo video-call filters deep-dive.

Canonical reading

Editorial annotations on why each piece matters — the articles themselves are the originals; read them there.

Building a video call app with filtersRitesh Shukla (Margelo)

Architecture deep-dive: real-time RN video-call filters (background blur, virtual backgrounds, Center Stage auto-zoom, live drawing) by injecting processed VisionCamera frames into LiveKit's WebRTC VideoSource without touching encoder/transport/signaling. Covers off-thread segmentation (MediaPipe/Vision), Metal/Skia compositing, YUV↔I420/NV12 conversion, and the monotonic-timestamp + buffer-pooling gotchas.

MoQKit: a native mobile SDK for MoQ on iOS and AndroidJakub Perżyło (Software Mansion)

Architectural intro to Media over QUIC for mobile — the session/namespace/track model, why moq-lite over moq-transport, and where it sits between WebRTC and HLS. A durable real-time-media transport reference.

What's New in VisionCamera V5?Marc Rousavy (Margelo; VisionCamera creator)

The canonical v5 reference for this entry's default camera pick: the full Nitro rewrite (−3k LOC of hand-written JSI, ~15x lower call latency vs Turbo Modules), the Constraints API replacing Formats, in-memory photo capture, depth/RAW/multi-cam, and modular frame-processor plugins. Read before betting real-time work on v5.

Scanning Barcodes in React Native Apps: The Complete Guide (2026)Marc Rousavy (creator of VisionCamera and react-native-data-scanner)

The cross-library selection guide the VisionCamera-scoped piece below doesn't cover, and its durable core is the ENGINE MAP: every RN scanning library wraps one of four native engines — Google ML Kit (VisionCamera's Barcode Scanner on both platforms, expo-camera on Android, data-scanner on Android), AVFoundation's AVCaptureMetadataOutput (VisionCamera's Object Output, expo-camera on iOS for most formats), VisionKit's DataScannerViewController (react-native-data-scanner on iOS 16+, expo-camera's launchScanner), and ZXing (which expo-camera bundles on iOS to decode PDF417/Code 39/Codabar). The consequence is the useful part: decoder quirks belong to the ENGINE, not the wrapper, so if ML Kit mis-reads an inverted barcode every ML-Kit-based library does, and iOS reporting UPC-A as EAN-13 shows up in everything built on Apple's APIs. The routing: one code and no custom UI → react-native-data-scanner (one call, no camera view to build); in-app camera with overlays or validation → VisionCamera + the barcode plugin; smallest iOS-only footprint → VisionCamera's Object Output; Expo Go → expo-camera; damaged/dense codes at industrial scale → a commercial SDK. Author bias is explicit (he wrote VisionCamera and data-scanner), and the post corrects a vendor claim that VisionCamera uses VisionKit — it never has. Note react-native-data-scanner is 0.1.2 on npm (verified 2026-08-18): the routing advice is sound, the package is early.

QR and Barcode Scanning in React Native with VisionCamera V5Marc Rousavy (Margelo; VisionCamera creator)

By VisionCamera's creator: three scanning APIs (CodeScanner / useBarcodeScannerOutput / frame processors), narrowing barcodeFormats and outputResolution for latency, the frame-vs-preview coordinate gotcha, and MLKit vs Apple's native AVCaptureMetadataOutput vs commercial SDKs. The reference for RN code scanning.

Sources

Depth (in-domain rules) is owned by the react-native-jsi skill — this entry is selection breadth.

The full explanation

The reviewed long-form essay behind this entry — the why, not a how-to. Also on GitHub.

About camera, video & real-time media (WebRTC, frame processing, filters)

Diataxis: Explanation. This page builds understanding of why capture, playback, and calling get different answers. It is not an integration guide. Frame-processor depth is owned by react-native-jsi; real-time discipline by rt-audio-pipeline-audit; the Nitro/JSI story by RB-E-NATIVE. A new, lightly vetted domain (confidence: low) — picks are starting points to prototype on-device.

The one principle that organises everything: three problems, one pipeline you must not fork

"Media" is three problems wearing one name. Capture is a camera problem. Playback is a decoding-and-display problem. Calling is a transport problem you almost always buy as an SDK. The durable lesson at the hardest intersection — real-time effects inside a call — is: don't fork the WebRTC stack. Intercept camera frames, run segmentation/detection off the hot path (~20–30ms, so "latest mask wins" rather than blocking), GPU-composite, and inject back into the existing WebRTC VideoSource — the source doesn't care who produces frames, so you get filters without touching encoder, transport, or signaling.

The default, and why

Camera → VisionCamera (frame processors) or expo-camera (managed); video calling → LiveKit (open-source) or a hosted SDK (Daily/Agora/Stream); real-time filters → process VisionCamera frames + GPU-composite and inject into the WebRTC source; image display → expo-image over core <Image>. Calling-SDK specifics are lightly vetted (confidence: low) — prototype perf per-device before committing.

Each sub-problem gets its own tool. VisionCamera because its JSI frame processors are the base for custom real-time frame pipelines (v5 rebuilt on Nitro, consistent with RB-E-NATIVE); expo-camera when the job is just capturing photos or scanning codes. Calling goes through an SDK because raw react-native-webrtc is low-level (you wire signaling/SFU yourself) and patch-only on Chromium M124 (124.0.8, 2026-07-21, is the fifth 124.0.x patch since 2024-08, while LiveKit's own fork is on M144) — which strengthens the SDK-over-raw-WebRTC advice; LiveKit is the open-source, self-hostable pick, Daily/Agora/Stream the hosted ones. The caveat is part of the default, not an apology.

The landscape, and when each piece earns its place

VisionCamera (v5, Nitro) — the RN camera with JSI frame processors; the base for custom real-time pipelines. Timing matters: v5 went GA 2026-04 and is still stabilizing — open iOS-26 teardown/race crashes plus an AE regression vs v4, with ~85% of installs still on v4 (2026-07). Ship it pinned-patch with a device-matrix pass; don't linger on v4 either (frozen since 2025-11).

expo-camera — managed camera for Expo apps; simpler, fewer real-time hooks; the capture / scan-codes answer.

react-native-webrtc — low-level bindings; patch-only on M124 (above); inject into, don't build on.

LiveKit / Daily / Agora / Stream Video — WebRTC conferencing SDKs (RN + web). LiveKit open-source + self-hostable; the others hosted infra. @livekit/react-native 3.0.0 (2026-09-11) peers LiveKit's own WebRTC fork (@livekit/react-native-webrtc ^144.2.0) and moves to LiveKit-prefixed WebRTC builds so it no longer collides with another WebRTC implementation in the same app.

Native call UI (CallKit / Android telecom) — the incoming-call screen, hold/mute and VoIP push are a separate layer from media. react-native-callkeep is dormant (last publish 2024-11-18) and built on the pre-Jetpack android.telecom.ConnectionService, and Android hands every FCM push to a single service per app, so a VoIP call push fights your existing notification library for that slot. The working alternatives parse VoIP pushes natively and relay the rest: on Expo, expo-callkit-telecom (0.5.0, Jetpack Core Telecom, needs expo-notifications and peers LiveKit's WebRTC fork); otherwise the calling SDK's own integration (Fishjam's RN client bundles call UI + push parsing).

Skia / Metal + MediaPipe / Vision / ML Kit — the effects layer: GPU compositing (Skia/Metal) plus on-device segmentation/detection (MediaPipe on Android, Vision on iOS) for filters/effects.

expo-video / expo-audio — media playback (not capture or calling); replace deprecated expo-av (out of Expo Go in SDK 55, unpatched); expo-video splits VideoPlayer (player logic) from VideoView (view).

expo-image — image display; the modern replacement for core <Image>: disk/memory caching, blurhash/thumbhash placeholders, transitions, faster decode. Works in bare RN too.

react-native-audio-api (Software Mansion) — Web Audio API for RN: audio graphs, effects/filters via MediaElementAudioSourceNode; 0.13 (2026-07, verified vs npm) ships a stable <Audio/> component. The audio-processing layer — playback-only needs go to expo-audio.

Tradeoffs and failure modes to name out loud

  • Forking the pipeline for filters. Rebuilding encoder/transport/signaling to add effects is the anti-pattern this entry exists to prevent; inject into the existing VideoSource instead.
  • Blocking the hot path with ML. Segmentation runs ~20–30ms — latest mask wins; composite the freshest mask rather than stalling the frame.
  • Skipping the boring conversions. The reference architecture names YUV↔I420/NV12 conversion, monotonic timestamps, and buffer pooling as the real gotchas of frame injection.
  • Betting on v5 blind — or hiding on v4. v5 is stabilizing (crashes + AE regression above); adopt it pinned-patch with a device-matrix pass. But v4 is frozen, so staying is also a cost.
  • Staying on expo-av. Deprecated, unpatched, out of Expo Go since SDK 55 — migrate.
  • Raw react-native-webrtc as your calling foundation. DIY calling on a patch-only M124 layer.
  • react-native-callkeep for new call UI. Dormant, pre-Jetpack telecom, and blind to the one-FCM-service collision.
  • Over-trusting this entry. Confidence is low: options lightly vetted, calling-SDK pricing/features unverified — prototyping per-device is part of the recommendation.

How it interacts with the rest of the stack

  • Native modules (RB-E-NATIVE). VisionCamera v5's Nitro rewrite is part of the Nitro consolidation that entry tracks; frame processors are its JSI story applied to camera frames.
  • Frame-processor correctness (react-native-jsi). The defer skill — native-pipeline depth.
  • Real-time discipline (rt-audio-pipeline-audit). The hot-path discipline transfers from audio; the entry routes real-time correctness and perf review there.

In one paragraph

Camera, playback, and calling are three different problems: capture with VisionCamera (v5 on Nitro, GA 2026-04 but still stabilizing — pin a patch, run a device matrix) or expo-camera; play back with expo-video/expo-audio (expo-av is deprecated, out of Expo Go since SDK 55), display images with expo-image; buy calling as an SDK — LiveKit for open-source/self-hosted — because raw react-native-webrtc is patch-only on M124. For real-time effects the durable rule is don't fork the pipeline: segment off the hot path ("latest mask wins"), GPU-composite with Skia/Metal + MediaPipe/Vision, and inject processed VisionCamera frames into the existing WebRTC VideoSource. Confidence is low — prototype per-device first.


See also: RB-E-NATIVE (Nitro/JSI foundation for frame processors). Depth: the react-native-jsi skill and rt-audio-pipeline-audit (real-time discipline). Reference architecture: Margelo's video-call-filters deep-dive; on the horizon, per the entry's reading, MoQ (Media over QUIC) via Software Mansion's MoQKit — between WebRTC and HLS, not yet a pick.

Related in platform-native: native · storage · native-ui · keyboard · payments · brownfield · games · alt-frameworks