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.
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)
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
option
tradeoff
evidence
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-camera
Managed 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-webrtc
Low-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 Video
WebRTC 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 Kit
GPU compositing (Skia/Metal) + on-device segmentation/detection (MediaPipe on Android, Vision on iOS) for filters/effects. See note: FILTER PIPELINE.
expo-video / expo-audio
Media 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-image
Image 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.
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
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.
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.
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.
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.
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.
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.