react-native-webrtc-kaleidoscope 0.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -20
- package/android/build.gradle +67 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/assets/backgrounds/office-1.png +0 -0
- package/android/src/main/assets/backgrounds/office-2.png +0 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/EffectTuning.kt +57 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/KaleidoscopeModule.kt +43 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/Registration.kt +39 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/effects/BackgroundImageFactory.kt +335 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/effects/BlurFactory.kt +336 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/effects/MirrorFactory.kt +57 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/Egl.kt +88 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/Fbo.kt +61 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/GlDebug.kt +41 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/GlProgram.kt +74 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/GpuEffectFactory.kt +14 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/GpuEffectProcessor.kt +198 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/Shaders.kt +91 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/ShadersGenerated.kt +64 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/segmentation/Mask.kt +298 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/segmentation/MaskTuning.kt +34 -0
- package/app.plugin.d.ts +4 -0
- package/app.plugin.js +255 -0
- package/dist/backgrounds/index.d.ts +3 -0
- package/dist/backgrounds/index.d.ts.map +1 -0
- package/dist/backgrounds/index.js +6 -0
- package/dist/backgrounds/index.js.map +1 -0
- package/dist/backgrounds/office-1.d.ts +3 -0
- package/dist/backgrounds/office-1.d.ts.map +1 -0
- package/dist/backgrounds/office-1.js +5 -0
- package/dist/backgrounds/office-1.js.map +1 -0
- package/dist/backgrounds/office-1.web.d.ts +3 -0
- package/dist/backgrounds/office-1.web.d.ts.map +1 -0
- package/dist/backgrounds/office-1.web.js +8 -0
- package/dist/backgrounds/office-1.web.js.map +1 -0
- package/dist/backgrounds/office-1.webp +0 -0
- package/dist/backgrounds/office-2.d.ts +3 -0
- package/dist/backgrounds/office-2.d.ts.map +1 -0
- package/dist/backgrounds/office-2.js +5 -0
- package/dist/backgrounds/office-2.js.map +1 -0
- package/dist/backgrounds/office-2.web.d.ts +3 -0
- package/dist/backgrounds/office-2.web.d.ts.map +1 -0
- package/dist/backgrounds/office-2.web.js +8 -0
- package/dist/backgrounds/office-2.web.js.map +1 -0
- package/dist/backgrounds/office-2.webp +0 -0
- package/dist/backgrounds/preset-source.types.d.ts +2 -0
- package/dist/backgrounds/preset-source.types.d.ts.map +1 -0
- package/dist/backgrounds/preset-source.types.js +2 -0
- package/dist/backgrounds/preset-source.types.js.map +1 -0
- package/dist/backgrounds/presets.d.ts +3 -0
- package/dist/backgrounds/presets.d.ts.map +1 -0
- package/dist/backgrounds/presets.js +21 -0
- package/dist/backgrounds/presets.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/dist/index.web.d.ts +37 -0
- package/dist/index.web.d.ts.map +1 -0
- package/dist/index.web.js +97 -0
- package/dist/index.web.js.map +1 -0
- package/dist/livekit.d.ts +24 -0
- package/dist/livekit.d.ts.map +1 -0
- package/dist/livekit.js +57 -0
- package/dist/livekit.js.map +1 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/dist/web/effects/background-image.d.ts +3 -0
- package/dist/web/effects/background-image.d.ts.map +1 -0
- package/dist/web/effects/background-image.js +217 -0
- package/dist/web/effects/background-image.js.map +1 -0
- package/dist/web/effects/blur.d.ts +3 -0
- package/dist/web/effects/blur.d.ts.map +1 -0
- package/dist/web/effects/blur.js +241 -0
- package/dist/web/effects/blur.js.map +1 -0
- package/dist/web/effects/mirror.d.ts +3 -0
- package/dist/web/effects/mirror.d.ts.map +1 -0
- package/dist/web/effects/mirror.js +31 -0
- package/dist/web/effects/mirror.js.map +1 -0
- package/dist/web/effects/passthrough.d.ts +3 -0
- package/dist/web/effects/passthrough.d.ts.map +1 -0
- package/dist/web/effects/passthrough.js +15 -0
- package/dist/web/effects/passthrough.js.map +1 -0
- package/dist/web/insertable-streams.d.ts +36 -0
- package/dist/web/insertable-streams.d.ts.map +1 -0
- package/dist/web/insertable-streams.js +64 -0
- package/dist/web/insertable-streams.js.map +1 -0
- package/dist/web/segmenter.d.ts +20 -0
- package/dist/web/segmenter.d.ts.map +1 -0
- package/dist/web/segmenter.js +51 -0
- package/dist/web/segmenter.js.map +1 -0
- package/dist/web/shaders.d.ts +2 -0
- package/dist/web/shaders.d.ts.map +1 -0
- package/dist/web/shaders.generated.d.ts +4 -0
- package/dist/web/shaders.generated.d.ts.map +1 -0
- package/dist/web/shaders.generated.js +54 -0
- package/dist/web/shaders.generated.js.map +1 -0
- package/dist/web/shaders.js +32 -0
- package/dist/web/shaders.js.map +1 -0
- package/dist/web/tuning.d.ts +22 -0
- package/dist/web/tuning.d.ts.map +1 -0
- package/dist/web/tuning.js +43 -0
- package/dist/web/tuning.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/Kaleidoscope.podspec +100 -0
- package/ios/KaleidoscopeModule/EffectTuning.swift +73 -0
- package/ios/KaleidoscopeModule/KaleidoscopeModule.swift +32 -0
- package/ios/KaleidoscopeModule/Registration.swift +43 -0
- package/ios/KaleidoscopeModule/effects/BackgroundImageProcessor.swift +226 -0
- package/ios/KaleidoscopeModule/effects/BlurProcessor.swift +178 -0
- package/ios/KaleidoscopeModule/effects/FrameBridge.swift +40 -0
- package/ios/KaleidoscopeModule/effects/MirrorProcessor.swift +136 -0
- package/ios/KaleidoscopeModule/gpu/MetalRenderer.swift +423 -0
- package/ios/KaleidoscopeModule/gpu/ShaderLibrary.swift +100 -0
- package/ios/KaleidoscopeModule/gpu/TextureBridge.swift +120 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/office-1.png +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/office-2.png +0 -0
- package/ios/KaleidoscopeModule/segmentation/MaskTuning.swift +23 -0
- package/ios/KaleidoscopeModule/segmentation/Segmenter.swift +126 -0
- package/ios/KaleidoscopeModule/shaders/SHADERS.txt +5 -0
- package/ios/KaleidoscopeModule/shaders/blur.metalsrc +72 -0
- package/ios/KaleidoscopeModule/shaders/composite.metalsrc +22 -0
- package/ios/KaleidoscopeModule/shaders/nebula.metalsrc +72 -0
- package/ios/KaleidoscopeModule/shaders/passthrough.metalsrc +20 -0
- package/ios/KaleidoscopeModule/shaders/simianlights.metalsrc +72 -0
- package/package.json +83 -18
- package/src/backgrounds/README.md +78 -0
- package/src/backgrounds/assets.d.ts +7 -0
- package/src/backgrounds/index.ts +7 -0
- package/src/backgrounds/office-1.ts +6 -0
- package/src/backgrounds/office-1.web.ts +9 -0
- package/src/backgrounds/office-1.webp +0 -0
- package/src/backgrounds/office-2.ts +6 -0
- package/src/backgrounds/office-2.web.ts +9 -0
- package/src/backgrounds/office-2.webp +0 -0
- package/src/backgrounds/preset-source.types.ts +5 -0
- package/src/backgrounds/presets.ts +23 -0
- package/src/index.ts +177 -0
- package/src/index.web.ts +125 -0
- package/src/livekit.ts +65 -0
- package/src/types.ts +80 -0
- package/src/web/effects/background-image.ts +277 -0
- package/src/web/effects/blur.ts +291 -0
- package/src/web/effects/mirror.ts +37 -0
- package/src/web/effects/passthrough.ts +17 -0
- package/src/web/insertable-streams.ts +112 -0
- package/src/web/segmenter.ts +73 -0
- package/src/web/shaders.generated.ts +56 -0
- package/src/web/shaders.ts +31 -0
- package/src/web/tuning.ts +53 -0
package/README.md
CHANGED
|
@@ -1,12 +1,39 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./docs/kaleidoscope-logo.png" alt="react-native-webrtc-kaleidoscope logo" width="180" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# react-native-webrtc-kaleidoscope
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
[](#status)
|
|
8
|
+
[](https://www.npmjs.com/package/react-native-webrtc-kaleidoscope)
|
|
9
|
+
[](https://www.npmjs.com/package/react-native-webrtc-kaleidoscope)
|
|
10
|
+
[](https://github.com/simiancraft/react-native-webrtc-kaleidoscope/actions/workflows/ci.yml)
|
|
11
|
+
[](./LICENSE)
|
|
12
|
+
|
|
13
|
+
> Live video effects for `react-native-webrtc`, packaged as a managed-Expo-friendly Expo Module.
|
|
4
14
|
|
|
5
15
|
## Status
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
**Active development; not yet production-ready.** Published to npm at `1.0.0` (semantic-release cut the first tag at 1.0.0; the number reflects release automation, not a maturity claim). The npm presentation, marketing, and release-quality polish will come in a later pass; right now the README's job is to tell the truth about what works.
|
|
18
|
+
|
|
19
|
+
### What works today
|
|
20
|
+
|
|
21
|
+
- **Mirror** (horizontal flip).
|
|
22
|
+
- **Blur** (background blur, person stays sharp).
|
|
23
|
+
- **Background replacement** (composite a still PNG behind the segmented person; two bundled office presets, library-side asset pipeline).
|
|
24
|
+
- **Runtime tuning** of the GLSL effects; see the [Use](#use) section.
|
|
25
|
+
|
|
26
|
+
| Platform | Mirror | Blur | Background replacement | Notes |
|
|
27
|
+
|---|---|---|---|---|
|
|
28
|
+
| Web (Chrome / Edge) | ✓ | ✓ | ✓ | MediaStreamTrackProcessor + MediaPipe Selfie Segmentation (WASM, CDN) |
|
|
29
|
+
| Android (API 24+) | ✓ | ✓ | ✓ | OpenGL ES 3.0 + MLKit Selfie Segmentation |
|
|
30
|
+
| iOS (≥ 15) | ✓ | ✓ | ✓ | Metal + Vision (person segmentation), verified on device. Older A11 devices (iPhone X) run at a lower frame rate |
|
|
31
|
+
| Safari / Firefox | — | — | — | No Insertable Streams; `applyVideoEffects` throws a typed error |
|
|
8
32
|
|
|
9
|
-
|
|
33
|
+
### Coming soon
|
|
34
|
+
|
|
35
|
+
- **Procedural backgrounds** (animated shaders behind the person, not just still images). Same composite path; the only new piece is each effect's background producer.
|
|
36
|
+
- A careful pass over the npm presentation, install docs, and demo polish before any "we recommend you use this" framing.
|
|
10
37
|
|
|
11
38
|
## Install
|
|
12
39
|
|
|
@@ -16,6 +43,34 @@ bun add react-native-webrtc react-native-webrtc-kaleidoscope
|
|
|
16
43
|
|
|
17
44
|
`react-native-webrtc` is a peer dependency. Install it explicitly.
|
|
18
45
|
|
|
46
|
+
### Using LiveKit?
|
|
47
|
+
|
|
48
|
+
If your project uses `@livekit/react-native` it pulls in `@livekit/react-native-webrtc`, a fork of upstream `react-native-webrtc` that preserves the same `videoEffects` native classes and the `_setVideoEffects` JS API. Kaleidoscope works against either fork; the Android Gradle script picks whichever one your autolinking surfaced.
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
bun add @livekit/react-native @livekit/react-native-webrtc react-native-webrtc-kaleidoscope
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Pick one fork. Installing both upstream `react-native-webrtc` and `@livekit/react-native-webrtc` in the same app will cause native class collisions; that's the consumer's problem to resolve.
|
|
55
|
+
|
|
56
|
+
**Native wiring.** `@livekit/react-native` hands you a `LocalVideoTrack`; apply effects to its underlying `MediaStreamTrack`:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { applyVideoEffects } from 'react-native-webrtc-kaleidoscope';
|
|
60
|
+
|
|
61
|
+
applyVideoEffects(localCameraTrack.mediaStreamTrack, ['blur']);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Web wiring.** On web, LiveKit owns the `RTCRtpSender`, so you cannot swap the track yourself; go through LiveKit's processor API instead. The opt-in `/livekit` subpath ships a ready-made processor (it needs `livekit-client`, which a LiveKit app already has):
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { KaleidoscopeProcessor } from 'react-native-webrtc-kaleidoscope/livekit';
|
|
68
|
+
|
|
69
|
+
await localVideoTrack.setProcessor(new KaleidoscopeProcessor(['blur']), true);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The second argument shows the processed stream in your local preview. The processor tears down its Insertable-Streams pipeline on camera flip (`restart`) and unpublish (`destroy`), so repeated flips do not leak generators.
|
|
73
|
+
|
|
19
74
|
## Configure
|
|
20
75
|
|
|
21
76
|
Add the config plugin to `app.config.ts`:
|
|
@@ -23,11 +78,13 @@ Add the config plugin to `app.config.ts`:
|
|
|
23
78
|
```ts
|
|
24
79
|
export default {
|
|
25
80
|
expo: {
|
|
26
|
-
plugins: ['react-native-webrtc
|
|
81
|
+
plugins: ['react-native-webrtc-kaleidoscope'],
|
|
27
82
|
},
|
|
28
83
|
};
|
|
29
84
|
```
|
|
30
85
|
|
|
86
|
+
(`react-native-webrtc` 124.x does not ship a config plugin upstream; do not list it in `plugins`. If you are on a fork that adds one, add it explicitly.)
|
|
87
|
+
|
|
31
88
|
Then rebuild native code:
|
|
32
89
|
|
|
33
90
|
```sh
|
|
@@ -38,32 +95,77 @@ bunx expo prebuild
|
|
|
38
95
|
|
|
39
96
|
```ts
|
|
40
97
|
import { mediaDevices } from 'react-native-webrtc';
|
|
41
|
-
import {
|
|
98
|
+
import {
|
|
99
|
+
applyVideoEffects,
|
|
100
|
+
setBlurSigma,
|
|
101
|
+
setMaskHardness,
|
|
102
|
+
setMaskThreshold,
|
|
103
|
+
} from 'react-native-webrtc-kaleidoscope';
|
|
42
104
|
|
|
43
105
|
const stream = await mediaDevices.getUserMedia({ video: true });
|
|
44
106
|
const [track] = stream.getVideoTracks();
|
|
45
107
|
|
|
46
|
-
applyVideoEffects(track, ['
|
|
47
|
-
applyVideoEffects(track, ['
|
|
48
|
-
applyVideoEffects(track, []);
|
|
108
|
+
applyVideoEffects(track, ['mirror']);
|
|
109
|
+
applyVideoEffects(track, ['blur']);
|
|
110
|
+
applyVideoEffects(track, [{ name: 'background-image', source: 'office-1' }]);
|
|
111
|
+
applyVideoEffects(track, []); // clear all effects
|
|
112
|
+
|
|
113
|
+
// Runtime tuning (effects pick up the new values on the next frame):
|
|
114
|
+
setBlurSigma(25); // Gaussian σ; clamped to [0.5, 64], default 8.
|
|
115
|
+
setMaskHardness(0.2); // smoothstep transition width; clamped to [0, 1]. 0 = soft halo, 1 = near-step. Default 0.5.
|
|
116
|
+
setMaskThreshold(0.7); // smoothstep center; clamped to [0.05, 0.95]. Higher rejects low-confidence pixels. Default 0.5.
|
|
49
117
|
```
|
|
50
118
|
|
|
51
119
|
Effects chain in array order.
|
|
52
120
|
|
|
53
|
-
|
|
121
|
+
**Tuning note:** optimal values are platform-specific because each segmentation model (MediaPipe on web, MLKit on Android, Vision when iOS lands) produces a different confidence distribution. Working defaults on a typical well-lit scene:
|
|
54
122
|
|
|
55
|
-
| Platform |
|
|
123
|
+
| Platform | Blur sigma | Mask hardness | Mask threshold |
|
|
56
124
|
|---|---|---|---|
|
|
57
|
-
|
|
|
58
|
-
| Android
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
125
|
+
| Web (MediaPipe) | 25 | 0.2 | 0.85 |
|
|
126
|
+
| Android (MLKit) | 30 | 0.2 | 0.6 |
|
|
127
|
+
|
|
128
|
+
The library ships neutral defaults (8, 0.5, 0.5) and consumers tune at runtime via the API above; whether to ship the dialed-in values as platform-specific defaults is an open question waiting on iOS data.
|
|
129
|
+
|
|
130
|
+
## Web and native differences
|
|
131
|
+
|
|
132
|
+
The API surface is the same across platforms, but the runtimes differ in ways worth knowing before you wire effects in:
|
|
133
|
+
|
|
134
|
+
- **Effect parameters.** Web reads tuning from the global setters (`setBlurSigma`, `setMaskHardness`, `setMaskThreshold`) on the next frame. Native currently ignores per-call `EffectSpec` parameters such as `{ name: 'blur', sigma: 12 }`; tuning is global through the same setters. Per-call uniforms through the native registry are a follow-up.
|
|
135
|
+
- **Background source.** `background-image.source` is a bundled preset name on native (the upstream `_setVideoEffects` registry is keyed by flat strings, not URIs), but on web it accepts either a preset name or an arbitrary image URL or data URI.
|
|
136
|
+
- **Background presets ship as tree-shakeable files.** Two example backgrounds (`office-1`, `office-2`) are importable per preset: `import { office1 } from 'react-native-webrtc-kaleidoscope/backgrounds/office-1'`. Each preset is its own file behind its own subpath export, and the package sets `sideEffects: false`, so an unused preset is dropped by web bundlers — and, since Metro doesn't tree-shake, simply never imported on native. Web resolves the bundled WebP to a URL; native loads its own bundled copy by name. Web also still accepts an arbitrary image URL or data URI. See [`src/backgrounds/README.md`](./src/backgrounds/README.md).
|
|
137
|
+
- **Segmentation model on web.** The web blur and background-image effects load MediaPipe Selfie Segmentation from the jsdelivr CDN (`cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation`) on first use. A strict Content-Security-Policy must allow that origin for `script-src`, `connect-src`, and the WASM fetch, and the effects do not work offline. Mirror needs no model.
|
|
138
|
+
- **Browser support on web.** Effects use Insertable Streams (`MediaStreamTrackProcessor` and `MediaStreamTrackGenerator`), which ship in Chromium-based browsers; Safari and Firefox throw a typed capability error.
|
|
139
|
+
|
|
140
|
+
## What this isn't
|
|
141
|
+
|
|
142
|
+
- **Not a fork of `react-native-webrtc`.** A thin layer over its undocumented `_setVideoEffects` registry on native, and `MediaStreamTrackProcessor` on web. Install alongside `react-native-webrtc`.
|
|
143
|
+
- **Not a managed cloud SaaS.** Effects run locally on the device; the track stays peer-to-peer. No service, no API key, no per-minute billing.
|
|
144
|
+
- **Not a face-filter SDK.** Effects are background segmentation and frame transforms, not facial AR.
|
|
145
|
+
- **Not a streaming protocol replacement.** The transformed track plugs into the consumer's existing `RTCPeerConnection` pipeline.
|
|
146
|
+
|
|
147
|
+
## Architecture
|
|
148
|
+
|
|
149
|
+
The codebase lives across four surfaces:
|
|
150
|
+
|
|
151
|
+
- `src/` — JS facade and shared types. `applyVideoEffects(track, effects)` plus runtime tuning setters.
|
|
152
|
+
- `src/web/` — WebGL2 pipeline. MediaPipe segmentation + GLSL composite. One shader file per stage in `src/web/shaders.ts`.
|
|
153
|
+
- `android/` — OpenGL ES 3.0 pipeline. MLKit segmentation (async, worker-thread, last-known-mask cache) + GLSL composite. Shaders inline in `gpu/Shaders.kt` as `const val` strings.
|
|
154
|
+
- `ios/` — Metal pipeline (Swift) with Vision person segmentation. The canonical GLSL in `shaders/` transpiles to Metal Shading Language via `scripts/build-shaders.ts`. Implemented and verified on device.
|
|
155
|
+
|
|
156
|
+
The composite shader (`shaders/composite.frag`) is the same GLSL source for every effect category (blur, background-image, future procedural backgrounds). Per-effect difference is upstream of the composite: how the `uBackground` texture gets produced.
|
|
157
|
+
|
|
158
|
+
See [`PATTERNS.md`](./PATTERNS.md) for the file-layout conventions, texture-orientation contract, and recipe for adding new effects, shaders, presets, or tunable parameters.
|
|
62
159
|
|
|
63
160
|
## Reference
|
|
64
161
|
|
|
65
|
-
- [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
66
|
-
- [AGENTS.md](./AGENTS.md)
|
|
67
|
-
- [
|
|
68
|
-
- [
|
|
69
|
-
-
|
|
162
|
+
- [CONTRIBUTING.md](./CONTRIBUTING.md): setup, scripts, commit conventions.
|
|
163
|
+
- [AGENTS.md](./AGENTS.md): agent and contributor orientation.
|
|
164
|
+
- [PATTERNS.md](./PATTERNS.md): codebase conventions and how-to-extend.
|
|
165
|
+
- [SECURITY.md](./SECURITY.md): security policy and reporting.
|
|
166
|
+
- [NOTICE.md](./NOTICE.md): third-party attributions.
|
|
167
|
+
- Sibling projects: [chromonym](https://github.com/simiancraft/chromonym) and [unitforge](https://github.com/simiancraft/unitforge); same OSS-hygiene template.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
MIT licensed. © 2026 Jesse Harlin / [Simiancraft](https://github.com/simiancraft).
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Android build script for react-native-webrtc-kaleidoscope.
|
|
2
|
+
|
|
3
|
+
apply plugin: 'com.android.library'
|
|
4
|
+
apply plugin: 'kotlin-android'
|
|
5
|
+
|
|
6
|
+
group = 'com.simiancraft.kaleidoscope'
|
|
7
|
+
// Single source of truth for the version is package.json (the iOS podspec reads
|
|
8
|
+
// it the same way), so the three platforms cannot drift apart.
|
|
9
|
+
version = new groovy.json.JsonSlurper().parseText(file("../package.json").text).version
|
|
10
|
+
|
|
11
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
12
|
+
if (expoModulesCorePlugin.exists()) {
|
|
13
|
+
apply from: expoModulesCorePlugin
|
|
14
|
+
applyKotlinExpoModulesCorePlugin()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
android {
|
|
18
|
+
namespace 'com.simiancraft.kaleidoscope'
|
|
19
|
+
compileSdkVersion safeExtGet('compileSdkVersion', 34)
|
|
20
|
+
|
|
21
|
+
defaultConfig {
|
|
22
|
+
minSdkVersion safeExtGet('minSdkVersion', 24)
|
|
23
|
+
targetSdkVersion safeExtGet('targetSdkVersion', 34)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
compileOptions {
|
|
27
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
28
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
kotlinOptions {
|
|
32
|
+
jvmTarget = '17'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
dependencies {
|
|
37
|
+
implementation project(':expo-modules-core')
|
|
38
|
+
|
|
39
|
+
// react-native-webrtc is a peer dependency: link against its types
|
|
40
|
+
// (ProcessorProvider, VideoFrameProcessor, VideoFrameProcessorFactoryInterface,
|
|
41
|
+
// and the org.webrtc.* surface it bundles) at compile time only; the consumer
|
|
42
|
+
// app provides the runtime via autolinking.
|
|
43
|
+
//
|
|
44
|
+
// Two forks ship the same classes under `com.oney.WebRTCModule.videoEffects.*`:
|
|
45
|
+
// - react-native-webrtc/react-native-webrtc (upstream)
|
|
46
|
+
// - livekit/react-native-webrtc (LiveKit fork, required by @livekit/react-native)
|
|
47
|
+
// Pick whichever the consumer's autolinking surfaced; throw if neither is present.
|
|
48
|
+
def rnWebrtcCandidates = [':react-native-webrtc', ':livekit_react-native-webrtc']
|
|
49
|
+
def rnWebrtcProject = rnWebrtcCandidates.find { rootProject.findProject(it) != null }
|
|
50
|
+
if (rnWebrtcProject == null) {
|
|
51
|
+
throw new GradleException(
|
|
52
|
+
'react-native-webrtc-kaleidoscope: install either react-native-webrtc ' +
|
|
53
|
+
'or @livekit/react-native-webrtc as a peer dependency.'
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
compileOnly project(rnWebrtcProject)
|
|
57
|
+
|
|
58
|
+
// Person/background mask for the blur effect. The Google artifact ID is
|
|
59
|
+
// `segmentation-selfie`, not `selfie-segmentation` (the plan and earlier
|
|
60
|
+
// notes had it inverted; that is why the pin would not resolve).
|
|
61
|
+
implementation 'com.google.mlkit:segmentation-selfie:16.0.0-beta6'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Helper: gracefully read root project ext properties with a default.
|
|
65
|
+
def safeExtGet(prop, fallback) {
|
|
66
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
67
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Mutable runtime parameters for the GLSL effects on Android. The Expo
|
|
2
|
+
// Module's setBlurSigma / setMaskHardness JS functions update these values;
|
|
3
|
+
// per-frame processors read them on every frame so changes take effect
|
|
4
|
+
// without re-registering processors.
|
|
5
|
+
//
|
|
6
|
+
// This is the parameter-passing side-channel that lets us tune effect
|
|
7
|
+
// uniforms without touching the upstream react-native-webrtc registry,
|
|
8
|
+
// which only accepts flat-string effect names. The web side mirrors this
|
|
9
|
+
// shape in src/web/tuning.ts; iOS mirrors it in
|
|
10
|
+
// ios/KaleidoscopeModule/EffectTuning.swift.
|
|
11
|
+
|
|
12
|
+
package com.simiancraft.kaleidoscope
|
|
13
|
+
|
|
14
|
+
internal object EffectTuning {
|
|
15
|
+
/**
|
|
16
|
+
* Gaussian sigma for the blur effect; higher = softer blur. Default
|
|
17
|
+
* matches the v0.1 hardcoded value. Custom setter clamps to a sane
|
|
18
|
+
* range; setting the property is the public mutation API.
|
|
19
|
+
*/
|
|
20
|
+
@Volatile
|
|
21
|
+
var blurSigma: Float = 8f
|
|
22
|
+
set(value) {
|
|
23
|
+
field = value.coerceIn(0.5f, 64f)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Mask smoothstep hardness for blur and background-image composites,
|
|
28
|
+
* in [0, 1]; 0 = soft halo, 1 = near-step. Default reproduces the
|
|
29
|
+
* historical smoothstep(0.34, 0.66) edge.
|
|
30
|
+
*/
|
|
31
|
+
@Volatile
|
|
32
|
+
var maskHardness: Float = 0.5f
|
|
33
|
+
set(value) {
|
|
34
|
+
field = value.coerceIn(0f, 1f)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Mask smoothstep center for blur and background-image composites,
|
|
39
|
+
* in [0, 1]; the threshold at which a pixel's raw confidence flips from
|
|
40
|
+
* "background" to "person". Default 0.5 reproduces the historical
|
|
41
|
+
* smoothstep centered on the confidence midpoint. Higher values reject
|
|
42
|
+
* low-confidence edges (chair backs, hair flyaway); lower values are
|
|
43
|
+
* more inclusive. Clamped to a workable range below to keep the
|
|
44
|
+
* smoothstep transition non-degenerate.
|
|
45
|
+
*/
|
|
46
|
+
@Volatile
|
|
47
|
+
var maskThreshold: Float = 0.5f
|
|
48
|
+
set(value) {
|
|
49
|
+
field = value.coerceIn(0.05f, 0.95f)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fun reset() {
|
|
53
|
+
blurSigma = 8f
|
|
54
|
+
maskHardness = 0.5f
|
|
55
|
+
maskThreshold = 0.5f
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Expo Module entry point for react-native-webrtc-kaleidoscope on Android.
|
|
2
|
+
// Calls Registration.registerAll(context) at module init so frame-processor
|
|
3
|
+
// factories land in ProcessorProvider before any track requests them.
|
|
4
|
+
//
|
|
5
|
+
// Also exposes setBlurSigma / setMaskHardness / resetEffectTuning JS
|
|
6
|
+
// functions that mutate com.simiancraft.kaleidoscope.EffectTuning at
|
|
7
|
+
// runtime; the per-frame processors read those values each frame, so
|
|
8
|
+
// changes take effect without re-registering or rebuilding processors.
|
|
9
|
+
// This side-channels the upstream react-native-webrtc registry, which only
|
|
10
|
+
// accepts flat-string effect names; spec parameters flow through here.
|
|
11
|
+
|
|
12
|
+
package com.simiancraft.kaleidoscope
|
|
13
|
+
|
|
14
|
+
import expo.modules.kotlin.modules.Module
|
|
15
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
16
|
+
|
|
17
|
+
class KaleidoscopeModule : Module() {
|
|
18
|
+
override fun definition() = ModuleDefinition {
|
|
19
|
+
Name("RnWebrtcKaleidoscope")
|
|
20
|
+
|
|
21
|
+
OnCreate {
|
|
22
|
+
val ctx = appContext.reactContext
|
|
23
|
+
?: error("Kaleidoscope: no react context at OnCreate; cannot register Android effects")
|
|
24
|
+
Registration.registerAll(ctx)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Function("setBlurSigma") { value: Float ->
|
|
28
|
+
EffectTuning.blurSigma = value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
Function("setMaskHardness") { value: Float ->
|
|
32
|
+
EffectTuning.maskHardness = value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
Function("setMaskThreshold") { value: Float ->
|
|
36
|
+
EffectTuning.maskThreshold = value
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Function("resetEffectTuning") {
|
|
40
|
+
EffectTuning.reset()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Frame-processor registration for Android. Called from
|
|
2
|
+
// KaleidoscopeModule.OnCreate at Expo Module init time, before any track
|
|
3
|
+
// requests an effect by name. The Context is needed by GPU effects so they
|
|
4
|
+
// can read PNG assets for background-image and create RenderScript-style
|
|
5
|
+
// resources where applicable.
|
|
6
|
+
|
|
7
|
+
package com.simiancraft.kaleidoscope
|
|
8
|
+
|
|
9
|
+
import android.content.Context
|
|
10
|
+
import com.oney.WebRTCModule.videoEffects.ProcessorProvider
|
|
11
|
+
import com.simiancraft.kaleidoscope.effects.BackgroundImageFactory
|
|
12
|
+
import com.simiancraft.kaleidoscope.effects.BlurFactory
|
|
13
|
+
import com.simiancraft.kaleidoscope.effects.MirrorFactory
|
|
14
|
+
import com.simiancraft.kaleidoscope.gpu.GpuEffectFactory
|
|
15
|
+
|
|
16
|
+
object Registration {
|
|
17
|
+
@JvmStatic
|
|
18
|
+
fun registerAll(context: Context) {
|
|
19
|
+
ProcessorProvider.addProcessor("mirror", MirrorFactory())
|
|
20
|
+
ProcessorProvider.addProcessor("blur", BlurFactory(context))
|
|
21
|
+
|
|
22
|
+
// Background-image variants — one factory per source preset. JS side
|
|
23
|
+
// emits "background-image-{source}" so each preset gets its own
|
|
24
|
+
// ProcessorProvider entry. Parameterized dispatch via uniforms lands
|
|
25
|
+
// when we extend the upstream rn-webrtc API surface.
|
|
26
|
+
ProcessorProvider.addProcessor(
|
|
27
|
+
"background-image-office-1",
|
|
28
|
+
BackgroundImageFactory(context, "office-1"),
|
|
29
|
+
)
|
|
30
|
+
ProcessorProvider.addProcessor(
|
|
31
|
+
"background-image-office-2",
|
|
32
|
+
BackgroundImageFactory(context, "office-2"),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
// Temporary architecture-proof passthrough hook from the GPU bring-up.
|
|
36
|
+
// Removed in the cleanup pass before v0.1 ships.
|
|
37
|
+
ProcessorProvider.addProcessor("gpu-passthrough", GpuEffectFactory())
|
|
38
|
+
}
|
|
39
|
+
}
|