react-native-webrtc-kaleidoscope 1.0.0 → 2.0.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/NOTICE.md +17 -6
- package/README.md +67 -19
- package/android/build.gradle +6 -5
- package/android/src/main/assets/backgrounds/dark-office.webp +0 -0
- package/android/src/main/assets/backgrounds/debug-resolutions.webp +0 -0
- package/android/src/main/assets/backgrounds/home-dark.webp +0 -0
- package/android/src/main/assets/backgrounds/home-light.webp +0 -0
- package/android/src/main/assets/backgrounds/light-office.webp +0 -0
- package/android/src/main/assets/backgrounds/nature-dark.webp +0 -0
- package/android/src/main/assets/backgrounds/nature-light.webp +0 -0
- package/android/src/main/assets/backgrounds/simiancraft-dark.webp +0 -0
- package/android/src/main/assets/backgrounds/simiancraft-light.webp +0 -0
- package/android/src/main/assets/backgrounds/stylized-dark.webp +0 -0
- package/android/src/main/assets/backgrounds/stylized-light.webp +0 -0
- package/android/src/main/assets/selfie_segmenter.tflite +0 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/EffectTuning.kt +41 -10
- package/android/src/main/java/com/simiancraft/kaleidoscope/KaleidoscopeModule.kt +8 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/Registration.kt +52 -13
- package/android/src/main/java/com/simiancraft/kaleidoscope/effects/BackgroundImageFactory.kt +53 -23
- package/android/src/main/java/com/simiancraft/kaleidoscope/effects/BlurFactory.kt +114 -50
- package/android/src/main/java/com/simiancraft/kaleidoscope/effects/TransformFactory.kt +277 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/FramePipeline.kt +158 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/GlProgram.kt +5 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/Ingest.kt +123 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/Orientation.kt +58 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/Shaders.kt +23 -51
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/ShadersGenerated.kt +76 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/segmentation/Mask.kt +100 -89
- package/android/src/main/java/com/simiancraft/kaleidoscope/segmentation/SegmentationEngine.kt +148 -0
- package/app.plugin.js +254 -4
- package/dist/backgrounds/dark-office.d.ts +3 -0
- package/dist/backgrounds/dark-office.d.ts.map +1 -0
- package/dist/backgrounds/dark-office.js +5 -0
- package/dist/backgrounds/dark-office.js.map +1 -0
- package/dist/backgrounds/dark-office.web.d.ts +3 -0
- package/dist/backgrounds/dark-office.web.d.ts.map +1 -0
- package/dist/backgrounds/dark-office.web.js +8 -0
- package/dist/backgrounds/dark-office.web.js.map +1 -0
- package/dist/backgrounds/dark-office.webp +0 -0
- package/dist/backgrounds/debug-resolutions.d.ts +3 -0
- package/dist/backgrounds/debug-resolutions.d.ts.map +1 -0
- package/dist/backgrounds/debug-resolutions.js +6 -0
- package/dist/backgrounds/debug-resolutions.js.map +1 -0
- package/dist/backgrounds/debug-resolutions.web.d.ts +3 -0
- package/dist/backgrounds/debug-resolutions.web.d.ts.map +1 -0
- package/dist/backgrounds/debug-resolutions.web.js +8 -0
- package/dist/backgrounds/debug-resolutions.web.js.map +1 -0
- package/dist/backgrounds/debug-resolutions.webp +0 -0
- package/dist/backgrounds/home-dark.d.ts +3 -0
- package/dist/backgrounds/home-dark.d.ts.map +1 -0
- package/dist/backgrounds/home-dark.js +5 -0
- package/dist/backgrounds/home-dark.js.map +1 -0
- package/dist/backgrounds/home-dark.web.d.ts +3 -0
- package/dist/backgrounds/home-dark.web.d.ts.map +1 -0
- package/dist/backgrounds/home-dark.web.js +6 -0
- package/dist/backgrounds/home-dark.web.js.map +1 -0
- package/dist/backgrounds/home-dark.webp +0 -0
- package/dist/backgrounds/home-light.d.ts +3 -0
- package/dist/backgrounds/home-light.d.ts.map +1 -0
- package/dist/backgrounds/home-light.js +5 -0
- package/dist/backgrounds/home-light.js.map +1 -0
- package/dist/backgrounds/home-light.web.d.ts +3 -0
- package/dist/backgrounds/home-light.web.d.ts.map +1 -0
- package/dist/backgrounds/home-light.web.js +6 -0
- package/dist/backgrounds/home-light.web.js.map +1 -0
- package/dist/backgrounds/home-light.webp +0 -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/light-office.d.ts +3 -0
- package/dist/backgrounds/light-office.d.ts.map +1 -0
- package/dist/backgrounds/light-office.js +5 -0
- package/dist/backgrounds/light-office.js.map +1 -0
- package/dist/backgrounds/light-office.web.d.ts +3 -0
- package/dist/backgrounds/light-office.web.d.ts.map +1 -0
- package/dist/backgrounds/light-office.web.js +8 -0
- package/dist/backgrounds/light-office.web.js.map +1 -0
- package/dist/backgrounds/light-office.webp +0 -0
- package/dist/backgrounds/nature-dark.d.ts +3 -0
- package/dist/backgrounds/nature-dark.d.ts.map +1 -0
- package/dist/backgrounds/nature-dark.js +5 -0
- package/dist/backgrounds/nature-dark.js.map +1 -0
- package/dist/backgrounds/nature-dark.web.d.ts +3 -0
- package/dist/backgrounds/nature-dark.web.d.ts.map +1 -0
- package/dist/backgrounds/nature-dark.web.js +6 -0
- package/dist/backgrounds/nature-dark.web.js.map +1 -0
- package/dist/backgrounds/nature-dark.webp +0 -0
- package/dist/backgrounds/nature-light.d.ts +3 -0
- package/dist/backgrounds/nature-light.d.ts.map +1 -0
- package/dist/backgrounds/nature-light.js +5 -0
- package/dist/backgrounds/nature-light.js.map +1 -0
- package/dist/backgrounds/nature-light.web.d.ts +3 -0
- package/dist/backgrounds/nature-light.web.d.ts.map +1 -0
- package/dist/backgrounds/nature-light.web.js +6 -0
- package/dist/backgrounds/nature-light.web.js.map +1 -0
- package/dist/backgrounds/nature-light.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 +34 -0
- package/dist/backgrounds/presets.js.map +1 -0
- package/dist/backgrounds/simiancraft-dark.d.ts +3 -0
- package/dist/backgrounds/simiancraft-dark.d.ts.map +1 -0
- package/dist/backgrounds/simiancraft-dark.js +5 -0
- package/dist/backgrounds/simiancraft-dark.js.map +1 -0
- package/dist/backgrounds/simiancraft-dark.web.d.ts +3 -0
- package/dist/backgrounds/simiancraft-dark.web.d.ts.map +1 -0
- package/dist/backgrounds/simiancraft-dark.web.js +6 -0
- package/dist/backgrounds/simiancraft-dark.web.js.map +1 -0
- package/dist/backgrounds/simiancraft-dark.webp +0 -0
- package/dist/backgrounds/simiancraft-light.d.ts +3 -0
- package/dist/backgrounds/simiancraft-light.d.ts.map +1 -0
- package/dist/backgrounds/simiancraft-light.js +5 -0
- package/dist/backgrounds/simiancraft-light.js.map +1 -0
- package/dist/backgrounds/simiancraft-light.web.d.ts +3 -0
- package/dist/backgrounds/simiancraft-light.web.d.ts.map +1 -0
- package/dist/backgrounds/simiancraft-light.web.js +6 -0
- package/dist/backgrounds/simiancraft-light.web.js.map +1 -0
- package/dist/backgrounds/simiancraft-light.webp +0 -0
- package/dist/backgrounds/stylized-dark.d.ts +3 -0
- package/dist/backgrounds/stylized-dark.d.ts.map +1 -0
- package/dist/backgrounds/stylized-dark.js +5 -0
- package/dist/backgrounds/stylized-dark.js.map +1 -0
- package/dist/backgrounds/stylized-dark.web.d.ts +3 -0
- package/dist/backgrounds/stylized-dark.web.d.ts.map +1 -0
- package/dist/backgrounds/stylized-dark.web.js +6 -0
- package/dist/backgrounds/stylized-dark.web.js.map +1 -0
- package/dist/backgrounds/stylized-dark.webp +0 -0
- package/dist/backgrounds/stylized-light.d.ts +3 -0
- package/dist/backgrounds/stylized-light.d.ts.map +1 -0
- package/dist/backgrounds/stylized-light.js +5 -0
- package/dist/backgrounds/stylized-light.js.map +1 -0
- package/dist/backgrounds/stylized-light.web.d.ts +3 -0
- package/dist/backgrounds/stylized-light.web.d.ts.map +1 -0
- package/dist/backgrounds/stylized-light.web.js +6 -0
- package/dist/backgrounds/stylized-light.web.js.map +1 -0
- package/dist/backgrounds/stylized-light.webp +0 -0
- package/dist/index.d.ts +19 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +71 -22
- package/dist/index.js.map +1 -1
- package/dist/index.web.d.ts +25 -3
- package/dist/index.web.d.ts.map +1 -1
- package/dist/index.web.js +55 -16
- package/dist/index.web.js.map +1 -1
- 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 +20 -13
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +1 -1
- package/dist/web/blur-kernel.d.ts +6 -0
- package/dist/web/blur-kernel.d.ts.map +1 -0
- package/dist/web/blur-kernel.js +41 -0
- package/dist/web/blur-kernel.js.map +1 -0
- package/dist/web/effects/background-image.d.ts.map +1 -1
- package/dist/web/effects/background-image.js +100 -33
- package/dist/web/effects/background-image.js.map +1 -1
- package/dist/web/effects/blur.d.ts.map +1 -1
- package/dist/web/effects/blur.js +169 -38
- package/dist/web/effects/blur.js.map +1 -1
- package/dist/web/effects/transform.d.ts +4 -0
- package/dist/web/effects/transform.d.ts.map +1 -0
- package/dist/web/effects/transform.js +62 -0
- package/dist/web/effects/transform.js.map +1 -0
- package/dist/web/insertable-streams.d.ts +11 -1
- package/dist/web/insertable-streams.d.ts.map +1 -1
- package/dist/web/insertable-streams.js +22 -4
- package/dist/web/insertable-streams.js.map +1 -1
- package/dist/web/segmenter.d.ts +9 -0
- package/dist/web/segmenter.d.ts.map +1 -1
- package/dist/web/segmenter.js +32 -0
- package/dist/web/segmenter.js.map +1 -1
- package/dist/web/shaders.d.ts +1 -3
- package/dist/web/shaders.d.ts.map +1 -1
- 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 +29 -103
- package/dist/web/shaders.js.map +1 -1
- package/dist/web/tuning.d.ts +4 -0
- package/dist/web/tuning.d.ts.map +1 -1
- package/dist/web/tuning.js +17 -5
- package/dist/web/tuning.js.map +1 -1
- package/ios/Kaleidoscope.podspec +87 -14
- package/ios/KaleidoscopeModule/EffectTuning.swift +47 -7
- package/ios/KaleidoscopeModule/KaleidoscopeModule.swift +12 -0
- package/ios/KaleidoscopeModule/Registration.swift +78 -13
- package/ios/KaleidoscopeModule/effects/BackgroundImageProcessor.swift +293 -0
- package/ios/KaleidoscopeModule/effects/BlurProcessor.swift +232 -18
- package/ios/KaleidoscopeModule/effects/FrameBridge.swift +46 -0
- package/ios/KaleidoscopeModule/effects/TransformProcessor.swift +173 -0
- package/ios/KaleidoscopeModule/gpu/Ingest.swift +169 -0
- package/ios/KaleidoscopeModule/gpu/MetalRenderer.swift +601 -0
- package/ios/KaleidoscopeModule/gpu/Orientation.swift +83 -0
- package/ios/KaleidoscopeModule/gpu/ShaderLibrary.swift +100 -0
- package/ios/KaleidoscopeModule/gpu/TextureBridge.swift +291 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/dark-office.webp +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/debug-resolutions.webp +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/home-dark.webp +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/home-light.webp +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/light-office.webp +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/nature-dark.webp +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/nature-light.webp +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/simiancraft-dark.webp +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/simiancraft-light.webp +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/stylized-dark.webp +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/stylized-light.webp +0 -0
- package/ios/KaleidoscopeModule/resources/selfie_segmenter.tflite +0 -0
- package/ios/KaleidoscopeModule/segmentation/MaskTuning.swift +23 -0
- package/ios/KaleidoscopeModule/segmentation/Segmenter.swift +444 -25
- package/ios/KaleidoscopeModule/shaders/SHADERS.txt +6 -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/ios/KaleidoscopeModule/shaders/transform.metalsrc +22 -0
- package/package.json +120 -11
- package/src/backgrounds/README.md +83 -0
- package/src/backgrounds/assets.d.ts +7 -0
- package/src/backgrounds/dark-office.ts +6 -0
- package/src/backgrounds/dark-office.web.ts +9 -0
- package/src/backgrounds/dark-office.webp +0 -0
- package/src/backgrounds/debug-resolutions.ts +7 -0
- package/src/backgrounds/debug-resolutions.web.ts +9 -0
- package/src/backgrounds/debug-resolutions.webp +0 -0
- package/src/backgrounds/home-dark.ts +6 -0
- package/src/backgrounds/home-dark.web.ts +7 -0
- package/src/backgrounds/home-dark.webp +0 -0
- package/src/backgrounds/home-light.ts +6 -0
- package/src/backgrounds/home-light.web.ts +7 -0
- package/src/backgrounds/home-light.webp +0 -0
- package/src/backgrounds/index.ts +7 -0
- package/src/backgrounds/light-office.ts +6 -0
- package/src/backgrounds/light-office.web.ts +9 -0
- package/src/backgrounds/light-office.webp +0 -0
- package/src/backgrounds/nature-dark.ts +6 -0
- package/src/backgrounds/nature-dark.web.ts +7 -0
- package/src/backgrounds/nature-dark.webp +0 -0
- package/src/backgrounds/nature-light.ts +6 -0
- package/src/backgrounds/nature-light.web.ts +7 -0
- package/src/backgrounds/nature-light.webp +0 -0
- package/src/backgrounds/preset-source.types.ts +5 -0
- package/src/backgrounds/presets.ts +36 -0
- package/src/backgrounds/simiancraft-dark.ts +6 -0
- package/src/backgrounds/simiancraft-dark.web.ts +7 -0
- package/src/backgrounds/simiancraft-dark.webp +0 -0
- package/src/backgrounds/simiancraft-light.ts +6 -0
- package/src/backgrounds/simiancraft-light.web.ts +7 -0
- package/src/backgrounds/simiancraft-light.webp +0 -0
- package/src/backgrounds/stylized-dark.ts +6 -0
- package/src/backgrounds/stylized-dark.web.ts +7 -0
- package/src/backgrounds/stylized-dark.webp +0 -0
- package/src/backgrounds/stylized-light.ts +6 -0
- package/src/backgrounds/stylized-light.web.ts +7 -0
- package/src/backgrounds/stylized-light.webp +0 -0
- package/src/index.ts +94 -29
- package/src/index.web.ts +69 -19
- package/src/livekit.ts +65 -0
- package/src/types.ts +23 -14
- package/src/web/blur-kernel.ts +44 -0
- package/src/web/effects/background-image.ts +121 -34
- package/src/web/effects/blur.ts +214 -40
- package/src/web/effects/transform.ts +69 -0
- package/src/web/insertable-streams.ts +33 -5
- package/src/web/segmenter.ts +34 -0
- package/src/web/shaders.generated.ts +56 -0
- package/src/web/shaders.ts +29 -106
- package/src/web/tuning.ts +19 -5
- 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/effects/MirrorFactory.kt +0 -57
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/GpuEffectFactory.kt +0 -14
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/GpuEffectProcessor.kt +0 -198
- package/dist/backgrounds.d.ts +0 -3
- package/dist/backgrounds.d.ts.map +0 -1
- package/dist/backgrounds.js +0 -21
- package/dist/backgrounds.js.map +0 -1
- package/dist/web/effects/mirror.d.ts +0 -3
- package/dist/web/effects/mirror.d.ts.map +0 -1
- package/dist/web/effects/mirror.js +0 -31
- package/dist/web/effects/mirror.js.map +0 -1
- package/dist/web/effects/passthrough.d.ts +0 -3
- package/dist/web/effects/passthrough.d.ts.map +0 -1
- package/dist/web/effects/passthrough.js +0 -15
- package/dist/web/effects/passthrough.js.map +0 -1
- package/ios/KaleidoscopeModule/effects/MirrorProcessor.swift +0 -17
- package/plugin/build/withKaleidoscope.d.ts +0 -3
- package/plugin/build/withKaleidoscope.js +0 -14
- package/plugin/build/withKaleidoscope.js.map +0 -1
- package/src/backgrounds.ts +0 -23
- package/src/web/effects/mirror.ts +0 -37
- package/src/web/effects/passthrough.ts +0 -17
package/NOTICE.md
CHANGED
|
@@ -6,11 +6,22 @@ react-native-webrtc-kaleidoscope ships under the MIT license (see [LICENSE](./LI
|
|
|
6
6
|
|
|
7
7
|
- **react-native-webrtc** — MIT license. <https://github.com/react-native-webrtc/react-native-webrtc>. The `track._setVideoEffects(...)` JS surface, the Android `ProcessorProvider` registry, and the iOS `RTCVideoFrameProcessor` protocol all originate upstream. This package is a consumer of, not a fork of, that codebase.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Segmentation backends (used by the `blur` and `background-image` effects)
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **MediaPipe
|
|
11
|
+
All three platforms run Google's MediaPipe selfie segmentation. The native targets use the MediaPipe Tasks Image Segmenter with a model bundled in this package; the web target uses the legacy MediaPipe Selfie Segmentation Solution loaded at runtime from a CDN.
|
|
12
|
+
|
|
13
|
+
- **MediaPipe Tasks Vision** (Android) — Apache License 2.0. The `com.google.mediapipe:tasks-vision` AAR, `ImageSegmenter` API. <https://github.com/google-ai-edge/mediapipe>.
|
|
14
|
+
- **MediaPipe Tasks Vision** (iOS) — Apache License 2.0. The `MediaPipeTasksVision` CocoaPod, `ImageSegmenter` API. <https://github.com/google-ai-edge/mediapipe>.
|
|
15
|
+
- **MediaPipe Selfie Segmentation** (web) — Apache License 2.0. The `@mediapipe/selfie_segmentation` Solution, loaded at runtime from `cdn.jsdelivr.net` (not bundled in this package). <https://github.com/google-ai-edge/mediapipe>.
|
|
16
|
+
|
|
17
|
+
### Bundled model
|
|
18
|
+
|
|
19
|
+
The native targets redistribute a TensorFlow Lite model inside the published package; it ships in `android/src/main/assets/selfie_segmenter.tflite` and in the iOS `Kaleidoscope.bundle`.
|
|
20
|
+
|
|
21
|
+
- **MediaPipe SelfieSegmenter** (`selfie_segmenter.tflite`) — distributed by Google as part of MediaPipe Solutions.
|
|
22
|
+
- Source: <https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite>
|
|
23
|
+
- Model card: <https://storage.googleapis.com/mediapipe-assets/Model%20Card%20MediaPipe%20Selfie%20Segmentation.pdf>
|
|
24
|
+
- The MediaPipe project and SDK are published by Google under the Apache License 2.0. The `selfie_segmenter.tflite` weights are redistributed here as published by Google as part of MediaPipe Solutions; Google does not attach a separate license to the hosted model file, so its use is governed by the model card above. The web target does not bundle this file; it fetches its model from the CDN at runtime.
|
|
14
25
|
|
|
15
26
|
## Algorithmic and architectural references
|
|
16
27
|
|
|
@@ -19,8 +30,8 @@ react-native-webrtc-kaleidoscope ships under the MIT license (see [LICENSE](./LI
|
|
|
19
30
|
|
|
20
31
|
## Trademarks
|
|
21
32
|
|
|
22
|
-
- "Apple", "
|
|
23
|
-
- "Google", "
|
|
33
|
+
- "Apple", "Core Image", "Metal", and related Apple marks are trademarks of Apple Inc. Used here as nominative references only.
|
|
34
|
+
- "Google", "MediaPipe", and "TensorFlow Lite" are trademarks of Google LLC. Used here as nominative references only.
|
|
24
35
|
- "WebRTC" is a project of the W3C and IETF.
|
|
25
36
|
|
|
26
37
|
This package is not affiliated with, endorsed by, or certified by Apple, Google, the W3C, or the `react-native-webrtc` project.
|
package/README.md
CHANGED
|
@@ -8,31 +8,32 @@
|
|
|
8
8
|
[](https://www.npmjs.com/package/react-native-webrtc-kaleidoscope)
|
|
9
9
|
[](https://www.npmjs.com/package/react-native-webrtc-kaleidoscope)
|
|
10
10
|
[](https://github.com/simiancraft/react-native-webrtc-kaleidoscope/actions/workflows/ci.yml)
|
|
11
|
+
[](https://codecov.io/gh/simiancraft/react-native-webrtc-kaleidoscope)
|
|
12
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/simiancraft/react-native-webrtc-kaleidoscope)
|
|
11
13
|
[](./LICENSE)
|
|
12
14
|
|
|
13
|
-
>
|
|
15
|
+
> Creative, shader-based video effects for React Native video calls: blur or replace a person's background, with more camera effects to come. Works with `react-native-webrtc` and LiveKit, managed-Expo-friendly.
|
|
14
16
|
|
|
15
17
|
## Status
|
|
16
18
|
|
|
17
|
-
**Active development; not yet production-ready.** Published to npm
|
|
19
|
+
**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
20
|
|
|
19
21
|
### What works today
|
|
20
22
|
|
|
21
|
-
- **
|
|
23
|
+
- **Transform** (flip X / flip Y / rotate 90° CW / CCW) — orientation utilities, identical across platforms.
|
|
22
24
|
- **Blur** (background blur, person stays sharp).
|
|
23
|
-
- **Background replacement** (composite a still
|
|
25
|
+
- **Background replacement** (composite a still image behind the segmented person; eleven bundled presets, plus arbitrary URLs on web; library-side asset pipeline).
|
|
24
26
|
- **Runtime tuning** of the GLSL effects; see the [Use](#use) section.
|
|
25
27
|
|
|
26
|
-
| Platform |
|
|
28
|
+
| Platform | Transform | Blur | Background replacement | Notes |
|
|
27
29
|
|---|---|---|---|---|
|
|
28
30
|
| Web (Chrome / Edge) | ✓ | ✓ | ✓ | MediaStreamTrackProcessor + MediaPipe Selfie Segmentation (WASM, CDN) |
|
|
29
|
-
| Android (API 24+) | ✓ | ✓ | ✓ | OpenGL ES 3.0 +
|
|
30
|
-
| iOS (≥ 15) |
|
|
31
|
-
| Safari / Firefox | — | — | — | No Insertable Streams; `applyVideoEffects` throws a
|
|
31
|
+
| Android (API 24+) | ✓ | ✓ | ✓ | OpenGL ES 3.0 + MediaPipe Selfie Segmentation (Tasks) |
|
|
32
|
+
| iOS (≥ 15) | ✓ | ✓ | ✓ | Metal + Vision (person segmentation), verified on device. Older A11 devices (iPhone X) run at a lower frame rate |
|
|
33
|
+
| Safari / Firefox | — | — | — | No Insertable Streams; `applyVideoEffects` throws a clear capability error |
|
|
32
34
|
|
|
33
35
|
### Coming soon
|
|
34
36
|
|
|
35
|
-
- **iOS support**, via the canonical GLSL transpiled to Metal Shading Language (`glslangValidator` → `spirv-cross --msl`). The pipeline is in the repo at `scripts/transpile-shaders.ts` and the GLSL canonical source lives in `shaders/`. The Swift host code that loads the metallib and runs the Metal pipeline is the next chunk of work.
|
|
36
37
|
- **Procedural backgrounds** (animated shaders behind the person, not just still images). Same composite path; the only new piece is each effect's background producer.
|
|
37
38
|
- A careful pass over the npm presentation, install docs, and demo polish before any "we recommend you use this" framing.
|
|
38
39
|
|
|
@@ -54,6 +55,24 @@ bun add @livekit/react-native @livekit/react-native-webrtc react-native-webrtc-k
|
|
|
54
55
|
|
|
55
56
|
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.
|
|
56
57
|
|
|
58
|
+
**Native wiring.** `@livekit/react-native` hands you a `LocalVideoTrack`; apply effects to its underlying `MediaStreamTrack`:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { applyVideoEffects } from 'react-native-webrtc-kaleidoscope';
|
|
62
|
+
|
|
63
|
+
applyVideoEffects(localCameraTrack.mediaStreamTrack, ['blur']);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**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):
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { KaleidoscopeProcessor } from 'react-native-webrtc-kaleidoscope/livekit';
|
|
70
|
+
|
|
71
|
+
await localVideoTrack.setProcessor(new KaleidoscopeProcessor(['blur']), true);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
|
|
57
76
|
## Configure
|
|
58
77
|
|
|
59
78
|
Add the config plugin to `app.config.ts`:
|
|
@@ -84,31 +103,60 @@ import {
|
|
|
84
103
|
setMaskHardness,
|
|
85
104
|
setMaskThreshold,
|
|
86
105
|
} from 'react-native-webrtc-kaleidoscope';
|
|
106
|
+
import { darkOffice } from 'react-native-webrtc-kaleidoscope/backgrounds/dark-office';
|
|
87
107
|
|
|
88
108
|
const stream = await mediaDevices.getUserMedia({ video: true });
|
|
89
109
|
const [track] = stream.getVideoTracks();
|
|
90
110
|
|
|
91
|
-
applyVideoEffects(track, ['
|
|
111
|
+
applyVideoEffects(track, ['flip-x']); // also flip-y, rotate-cw, rotate-ccw
|
|
92
112
|
applyVideoEffects(track, ['blur']);
|
|
93
|
-
applyVideoEffects(track, [{ name: 'background-image', source:
|
|
113
|
+
applyVideoEffects(track, [{ name: 'background-image', source: darkOffice }]);
|
|
94
114
|
applyVideoEffects(track, []); // clear all effects
|
|
95
115
|
|
|
96
116
|
// Runtime tuning (effects pick up the new values on the next frame):
|
|
97
|
-
setBlurSigma(
|
|
98
|
-
setMaskHardness(0.
|
|
99
|
-
setMaskThreshold(0.7); // smoothstep center; clamped to [0.05, 0.95]. Higher rejects low-confidence pixels. Default 0.
|
|
117
|
+
setBlurSigma(5); // Gaussian σ; clamped to [0.5, 7], default 5.
|
|
118
|
+
setMaskHardness(0.5); // smoothstep transition width; clamped to [0, 1]. 0 = soft halo, 1 = near-step. Default 0.5.
|
|
119
|
+
setMaskThreshold(0.7); // smoothstep center; clamped to [0.05, 0.95]. Higher rejects low-confidence pixels. Default 0.7.
|
|
100
120
|
```
|
|
101
121
|
|
|
102
122
|
Effects chain in array order.
|
|
103
123
|
|
|
104
|
-
**Tuning note:**
|
|
124
|
+
**Tuning note:** all three platforms run MediaPipe selfie segmentation (Tasks Image Segmenter on native, the Selfie Segmentation Solution on web), but optimal values still differ slightly because the input downscale and the API variant differ per platform, producing different confidence distributions. Working defaults on a typical well-lit scene:
|
|
105
125
|
|
|
106
126
|
| Platform | Blur sigma | Mask hardness | Mask threshold |
|
|
107
127
|
|---|---|---|---|
|
|
108
128
|
| Web (MediaPipe) | 25 | 0.2 | 0.85 |
|
|
109
|
-
| Android (
|
|
129
|
+
| Android (MediaPipe) | 30 | 0.2 | 0.6 |
|
|
130
|
+
|
|
131
|
+
The library ships defaults (5, 0.5, 0.7) and consumers tune at runtime via the API above; whether to ship the dialed-in per-platform values as defaults is an open question.
|
|
132
|
+
|
|
133
|
+
## Background presets
|
|
134
|
+
|
|
135
|
+
Eleven backgrounds ship for the `background-image` effect, imported per preset (e.g. `import { darkOffice } from 'react-native-webrtc-kaleidoscope/backgrounds/dark-office'`). On web a preset can also be any image URL or data URI; native resolves bundled preset names only.
|
|
136
|
+
|
|
137
|
+
| Theme | Light | Dark |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| Office | <img src="src/backgrounds/light-office.webp" width="220" alt="light-office" /> | <img src="src/backgrounds/dark-office.webp" width="220" alt="dark-office" /> |
|
|
140
|
+
| Home | <img src="src/backgrounds/home-light.webp" width="220" alt="home-light" /> | <img src="src/backgrounds/home-dark.webp" width="220" alt="home-dark" /> |
|
|
141
|
+
| Nature | <img src="src/backgrounds/nature-light.webp" width="220" alt="nature-light" /> | <img src="src/backgrounds/nature-dark.webp" width="220" alt="nature-dark" /> |
|
|
142
|
+
| Stylized | <img src="src/backgrounds/stylized-light.webp" width="220" alt="stylized-light" /> | <img src="src/backgrounds/stylized-dark.webp" width="220" alt="stylized-dark" /> |
|
|
143
|
+
| Simiancraft | <img src="src/backgrounds/simiancraft-light.webp" width="220" alt="simiancraft-light" /> | <img src="src/backgrounds/simiancraft-dark.webp" width="220" alt="simiancraft-dark" /> |
|
|
144
|
+
|
|
145
|
+
Plus **`debug-resolutions`**, a viewport/resolution calibration grid for verifying background cover-fit:
|
|
146
|
+
|
|
147
|
+
<img src="src/backgrounds/debug-resolutions.webp" width="220" alt="debug-resolutions" />
|
|
148
|
+
|
|
149
|
+
See [`src/backgrounds/README.md`](./src/backgrounds/README.md) for sizing and how to add a preset.
|
|
150
|
+
|
|
151
|
+
## Web and native differences
|
|
152
|
+
|
|
153
|
+
The API surface is the same across platforms, but the runtimes differ in ways worth knowing before you wire effects in:
|
|
110
154
|
|
|
111
|
-
|
|
155
|
+
- **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.
|
|
156
|
+
- **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.
|
|
157
|
+
- **Background presets ship as tree-shakeable files.** The bundled backgrounds (see [Background presets](#background-presets)) are importable per preset: `import { darkOffice } from 'react-native-webrtc-kaleidoscope/backgrounds/dark-office'`. 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).
|
|
158
|
+
- **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.
|
|
159
|
+
- **Browser support on web.** Effects use Insertable Streams (`MediaStreamTrackProcessor` and `MediaStreamTrackGenerator`), which ship in Chromium-based browsers (Chrome, Edge); Safari and Firefox lack the API, so `applyVideoEffects` throws a clear capability error and the demo falls back to the unprocessed track.
|
|
112
160
|
|
|
113
161
|
## What this isn't
|
|
114
162
|
|
|
@@ -123,8 +171,8 @@ The codebase lives across four surfaces:
|
|
|
123
171
|
|
|
124
172
|
- `src/` — JS facade and shared types. `applyVideoEffects(track, effects)` plus runtime tuning setters.
|
|
125
173
|
- `src/web/` — WebGL2 pipeline. MediaPipe segmentation + GLSL composite. One shader file per stage in `src/web/shaders.ts`.
|
|
126
|
-
- `android/` — OpenGL ES 3.0 pipeline.
|
|
127
|
-
- `ios/` —
|
|
174
|
+
- `android/` — OpenGL ES 3.0 pipeline. MediaPipe Tasks segmentation (async, worker-thread, last-known-mask cache) + GLSL composite. Shaders inline in `gpu/Shaders.kt` as `const val` strings.
|
|
175
|
+
- `ios/` — Metal pipeline (Swift) with MediaPipe Tasks segmentation (`selfie_segmenter.tflite`, the same model the Android target bundles). The canonical GLSL in `shaders/` transpiles to Metal Shading Language via `scripts/build-shaders.ts`. Implemented and verified on device.
|
|
128
176
|
|
|
129
177
|
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.
|
|
130
178
|
|
package/android/build.gradle
CHANGED
|
@@ -4,7 +4,9 @@ apply plugin: 'com.android.library'
|
|
|
4
4
|
apply plugin: 'kotlin-android'
|
|
5
5
|
|
|
6
6
|
group = 'com.simiancraft.kaleidoscope'
|
|
7
|
-
version
|
|
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
|
|
8
10
|
|
|
9
11
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
10
12
|
if (expoModulesCorePlugin.exists()) {
|
|
@@ -53,10 +55,9 @@ dependencies {
|
|
|
53
55
|
}
|
|
54
56
|
compileOnly project(rnWebrtcProject)
|
|
55
57
|
|
|
56
|
-
// Person/background mask
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
implementation 'com.google.mlkit:segmentation-selfie:16.0.0-beta6'
|
|
58
|
+
// Person/background mask: MediaPipe Tasks ImageSegmenter (Mask.kt), the same
|
|
59
|
+
// model family the web and iOS sides run.
|
|
60
|
+
implementation 'com.google.mediapipe:tasks-vision:0.10.14'
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
// Helper: gracefully read root project ext properties with a default.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -13,14 +13,14 @@ package com.simiancraft.kaleidoscope
|
|
|
13
13
|
|
|
14
14
|
internal object EffectTuning {
|
|
15
15
|
/**
|
|
16
|
-
* Gaussian sigma for the blur effect; higher = softer blur. Default
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Gaussian sigma for the blur effect; higher = softer blur. Default 5;
|
|
17
|
+
* clamped to [0.5, 7] (the useful range before the linear-sampled kernel
|
|
18
|
+
* truncates and bands). Setting the property is the public mutation API.
|
|
19
19
|
*/
|
|
20
20
|
@Volatile
|
|
21
|
-
var blurSigma: Float =
|
|
21
|
+
var blurSigma: Float = 5f
|
|
22
22
|
set(value) {
|
|
23
|
-
field = value.coerceIn(0.5f,
|
|
23
|
+
field = value.coerceIn(0.5f, 7f)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
@@ -37,21 +37,52 @@ internal object EffectTuning {
|
|
|
37
37
|
/**
|
|
38
38
|
* Mask smoothstep center for blur and background-image composites,
|
|
39
39
|
* in [0, 1]; the threshold at which a pixel's raw confidence flips from
|
|
40
|
-
* "background" to "person". Default 0.
|
|
41
|
-
*
|
|
40
|
+
* "background" to "person". Default 0.7 (dialed in post-optimization).
|
|
41
|
+
* Higher values reject
|
|
42
42
|
* low-confidence edges (chair backs, hair flyaway); lower values are
|
|
43
43
|
* more inclusive. Clamped to a workable range below to keep the
|
|
44
44
|
* smoothstep transition non-degenerate.
|
|
45
45
|
*/
|
|
46
46
|
@Volatile
|
|
47
|
-
var maskThreshold: Float = 0.
|
|
47
|
+
var maskThreshold: Float = 0.7f
|
|
48
48
|
set(value) {
|
|
49
49
|
field = value.coerceIn(0.05f, 0.95f)
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Debug GPU timing. When true, the GLES effect factories log per-frame
|
|
54
|
+
* GPU-completion latency under the "Perf" logcat tag (read a frame late via
|
|
55
|
+
* the frame-pipeline fence). Off by default; toggled from JS via the Expo
|
|
56
|
+
* Module's setDebugTiming so a tester can capture ground-truth numbers on a
|
|
57
|
+
* device build without rebuilding. Mirrors the iOS timing flag being added
|
|
58
|
+
* in parallel.
|
|
59
|
+
*/
|
|
60
|
+
@Volatile
|
|
61
|
+
var debugTiming: Boolean = false
|
|
62
|
+
set(value) {
|
|
63
|
+
field = value
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Segmentation input short-side (px). The mask is produced from an input
|
|
68
|
+
* downscaled to this; lower = cheaper segmentation, softer mask edge.
|
|
69
|
+
* Default 384; clamped [128, 1080]. The floor matches iOS and web
|
|
70
|
+
* (EffectTuning.swift, tuning.ts) so the JS setSegmentationTargetShortSide
|
|
71
|
+
* contract is identical across platforms; MediaPipe resizes the input to the
|
|
72
|
+
* model's own 256x256 internally, so a smaller input still segments. Tuned
|
|
73
|
+
* live from JS via setSegmentationTargetShortSide.
|
|
74
|
+
*/
|
|
75
|
+
@Volatile
|
|
76
|
+
var targetShortSide: Int = 384
|
|
77
|
+
set(value) {
|
|
78
|
+
field = value.coerceIn(128, 1080)
|
|
79
|
+
}
|
|
80
|
+
|
|
52
81
|
fun reset() {
|
|
53
|
-
blurSigma =
|
|
82
|
+
blurSigma = 5f
|
|
54
83
|
maskHardness = 0.5f
|
|
55
|
-
maskThreshold = 0.
|
|
84
|
+
maskThreshold = 0.7f
|
|
85
|
+
debugTiming = false
|
|
86
|
+
targetShortSide = 384
|
|
56
87
|
}
|
|
57
88
|
}
|
|
@@ -36,6 +36,14 @@ class KaleidoscopeModule : Module() {
|
|
|
36
36
|
EffectTuning.maskThreshold = value
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
Function("setDebugTiming") { value: Boolean ->
|
|
40
|
+
EffectTuning.debugTiming = value
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
Function("setSegmentationTargetShortSide") { value: Int ->
|
|
44
|
+
EffectTuning.targetShortSide = value
|
|
45
|
+
}
|
|
46
|
+
|
|
39
47
|
Function("resetEffectTuning") {
|
|
40
48
|
EffectTuning.reset()
|
|
41
49
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Frame-processor registration for Android. Called from
|
|
2
2
|
// KaleidoscopeModule.OnCreate at Expo Module init time, before any track
|
|
3
3
|
// requests an effect by name. The Context is needed by GPU effects so they
|
|
4
|
-
// can read
|
|
5
|
-
//
|
|
4
|
+
// can read bundled assets: the background-image WebP presets and the
|
|
5
|
+
// selfie_segmenter.tflite model (loaded via SegmentationEngine).
|
|
6
6
|
|
|
7
7
|
package com.simiancraft.kaleidoscope
|
|
8
8
|
|
|
@@ -10,30 +10,69 @@ import android.content.Context
|
|
|
10
10
|
import com.oney.WebRTCModule.videoEffects.ProcessorProvider
|
|
11
11
|
import com.simiancraft.kaleidoscope.effects.BackgroundImageFactory
|
|
12
12
|
import com.simiancraft.kaleidoscope.effects.BlurFactory
|
|
13
|
-
import com.simiancraft.kaleidoscope.effects.
|
|
14
|
-
import com.simiancraft.kaleidoscope.gpu.
|
|
13
|
+
import com.simiancraft.kaleidoscope.effects.TransformFactory
|
|
14
|
+
import com.simiancraft.kaleidoscope.gpu.Orientation
|
|
15
15
|
|
|
16
16
|
object Registration {
|
|
17
17
|
@JvmStatic
|
|
18
18
|
fun registerAll(context: Context) {
|
|
19
|
-
ProcessorProvider.addProcessor("mirror", MirrorFactory())
|
|
20
19
|
ProcessorProvider.addProcessor("blur", BlurFactory(context))
|
|
21
20
|
|
|
21
|
+
// Geometric reorientation effects. flip-x is the corrected screen-horizontal
|
|
22
|
+
// mirror (replaces the old "mirror" CPU effect). The rotation correction
|
|
23
|
+
// lives entirely in Orientation.kt; each registration just names its op.
|
|
24
|
+
ProcessorProvider.addProcessor("flip-x", TransformFactory(Orientation.Op.FLIP_X))
|
|
25
|
+
ProcessorProvider.addProcessor("flip-y", TransformFactory(Orientation.Op.FLIP_Y))
|
|
26
|
+
ProcessorProvider.addProcessor("rotate-cw", TransformFactory(Orientation.Op.ROTATE_CW))
|
|
27
|
+
ProcessorProvider.addProcessor("rotate-ccw", TransformFactory(Orientation.Op.ROTATE_CCW))
|
|
28
|
+
|
|
22
29
|
// Background-image variants — one factory per source preset. JS side
|
|
23
30
|
// emits "background-image-{source}" so each preset gets its own
|
|
24
31
|
// ProcessorProvider entry. Parameterized dispatch via uniforms lands
|
|
25
32
|
// when we extend the upstream rn-webrtc API surface.
|
|
26
33
|
ProcessorProvider.addProcessor(
|
|
27
|
-
"background-image-
|
|
28
|
-
BackgroundImageFactory(context, "
|
|
34
|
+
"background-image-debug-resolutions",
|
|
35
|
+
BackgroundImageFactory(context, "debug-resolutions"),
|
|
29
36
|
)
|
|
30
37
|
ProcessorProvider.addProcessor(
|
|
31
|
-
"background-image-office
|
|
32
|
-
BackgroundImageFactory(context, "office
|
|
38
|
+
"background-image-dark-office",
|
|
39
|
+
BackgroundImageFactory(context, "dark-office"),
|
|
40
|
+
)
|
|
41
|
+
ProcessorProvider.addProcessor(
|
|
42
|
+
"background-image-light-office",
|
|
43
|
+
BackgroundImageFactory(context, "light-office"),
|
|
44
|
+
)
|
|
45
|
+
ProcessorProvider.addProcessor(
|
|
46
|
+
"background-image-home-light",
|
|
47
|
+
BackgroundImageFactory(context, "home-light"),
|
|
48
|
+
)
|
|
49
|
+
ProcessorProvider.addProcessor(
|
|
50
|
+
"background-image-home-dark",
|
|
51
|
+
BackgroundImageFactory(context, "home-dark"),
|
|
52
|
+
)
|
|
53
|
+
ProcessorProvider.addProcessor(
|
|
54
|
+
"background-image-nature-light",
|
|
55
|
+
BackgroundImageFactory(context, "nature-light"),
|
|
56
|
+
)
|
|
57
|
+
ProcessorProvider.addProcessor(
|
|
58
|
+
"background-image-nature-dark",
|
|
59
|
+
BackgroundImageFactory(context, "nature-dark"),
|
|
60
|
+
)
|
|
61
|
+
ProcessorProvider.addProcessor(
|
|
62
|
+
"background-image-stylized-light",
|
|
63
|
+
BackgroundImageFactory(context, "stylized-light"),
|
|
64
|
+
)
|
|
65
|
+
ProcessorProvider.addProcessor(
|
|
66
|
+
"background-image-stylized-dark",
|
|
67
|
+
BackgroundImageFactory(context, "stylized-dark"),
|
|
68
|
+
)
|
|
69
|
+
ProcessorProvider.addProcessor(
|
|
70
|
+
"background-image-simiancraft-light",
|
|
71
|
+
BackgroundImageFactory(context, "simiancraft-light"),
|
|
72
|
+
)
|
|
73
|
+
ProcessorProvider.addProcessor(
|
|
74
|
+
"background-image-simiancraft-dark",
|
|
75
|
+
BackgroundImageFactory(context, "simiancraft-dark"),
|
|
33
76
|
)
|
|
34
|
-
|
|
35
|
-
// Architecture-proof hook from PLAN.md Commit 3. Removed in the cleanup
|
|
36
|
-
// pass before v0.1 ships.
|
|
37
|
-
ProcessorProvider.addProcessor("gpu-passthrough", GpuEffectFactory())
|
|
38
77
|
}
|
|
39
78
|
}
|
package/android/src/main/java/com/simiancraft/kaleidoscope/effects/BackgroundImageFactory.kt
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Per frame:
|
|
4
4
|
// 1. Render the input OES camera texture into a cached "original 2D" FBO.
|
|
5
|
-
// 2. Lazy-load the named
|
|
5
|
+
// 2. Lazy-load the named WebP asset (e.g. "dark-office") from the library's
|
|
6
6
|
// android/src/main/assets/backgrounds/ on first frame; upload as a 2D
|
|
7
7
|
// GL texture; cache for subsequent frames.
|
|
8
|
-
// 3. Produce a mask via Mask.produce (downsample,
|
|
8
|
+
// 3. Produce a mask via Mask.produce (downsample, MediaPipe, upload).
|
|
9
9
|
// 4. Composite original + image + mask into a fresh output texture via
|
|
10
10
|
// COMPOSITE_FRAG.
|
|
11
11
|
// 5. Wrap the fresh output texture in a TextureBufferImpl and return a
|
|
12
12
|
// VideoFrame.
|
|
13
13
|
//
|
|
14
|
-
// Each registered name (e.g. "background-image-office
|
|
14
|
+
// Each registered name (e.g. "background-image-dark-office") gets its own
|
|
15
15
|
// factory keyed by an asset name; multiple factories can coexist if the
|
|
16
16
|
// consumer registers multiple variants.
|
|
17
17
|
//
|
|
@@ -31,7 +31,9 @@ import com.oney.WebRTCModule.videoEffects.VideoFrameProcessorFactoryInterface
|
|
|
31
31
|
import com.simiancraft.kaleidoscope.EffectTuning
|
|
32
32
|
import com.simiancraft.kaleidoscope.gpu.Egl
|
|
33
33
|
import com.simiancraft.kaleidoscope.gpu.Fbo
|
|
34
|
+
import com.simiancraft.kaleidoscope.gpu.FramePipeline
|
|
34
35
|
import com.simiancraft.kaleidoscope.gpu.GlDebug
|
|
36
|
+
import com.simiancraft.kaleidoscope.gpu.Ingest
|
|
35
37
|
import com.simiancraft.kaleidoscope.gpu.GlProgram
|
|
36
38
|
import com.simiancraft.kaleidoscope.gpu.Shaders
|
|
37
39
|
import com.simiancraft.kaleidoscope.segmentation.Mask
|
|
@@ -43,8 +45,9 @@ import org.webrtc.YuvConverter
|
|
|
43
45
|
|
|
44
46
|
/**
|
|
45
47
|
* @param context Used to read the PNG asset.
|
|
46
|
-
* @param assetName Filename (without `.
|
|
47
|
-
* E.g. "office
|
|
48
|
+
* @param assetName Filename (without `.webp`) under `assets/backgrounds/`.
|
|
49
|
+
* E.g. "dark-office" -> assets/backgrounds/dark-office.webp.
|
|
50
|
+
* BitmapFactory decodes WebP natively (supported since API 14).
|
|
48
51
|
*
|
|
49
52
|
* maskHardness is read per frame from com.simiancraft.kaleidoscope.EffectTuning
|
|
50
53
|
* so JS callers can tune the smoothstep edge via the Expo Module's
|
|
@@ -62,6 +65,10 @@ private class BackgroundImageProcessor(
|
|
|
62
65
|
private val context: Context,
|
|
63
66
|
private val assetName: String,
|
|
64
67
|
) : VideoFrameProcessor {
|
|
68
|
+
// process() is only ever invoked on the single SurfaceTextureHelper capture
|
|
69
|
+
// thread (VideoEffectProcessor.onFrameCaptured), so this never actually
|
|
70
|
+
// contends. Retained as cheap uncontended insurance and as an explicit marker
|
|
71
|
+
// that the GL state below is single-threaded; it is NOT a cross-thread guard.
|
|
65
72
|
private val lock = Any()
|
|
66
73
|
|
|
67
74
|
private var oesToTwoD: GlProgram? = null
|
|
@@ -71,9 +78,13 @@ private class BackgroundImageProcessor(
|
|
|
71
78
|
private var cachedWidth = 0
|
|
72
79
|
private var cachedHeight = 0
|
|
73
80
|
|
|
74
|
-
private val mask = Mask()
|
|
81
|
+
private val mask = Mask(context)
|
|
75
82
|
private var yuvConverter: YuvConverter? = null
|
|
76
83
|
|
|
84
|
+
// R3: one-frame GPU pipeline (see FramePipeline). Replaces the per-frame
|
|
85
|
+
// glFinish so the capture thread does not block on this frame's GPU work.
|
|
86
|
+
private val pipeline = FramePipeline()
|
|
87
|
+
|
|
77
88
|
// Background image cached as a 2D GL texture; loaded lazily on the first
|
|
78
89
|
// successful frame to ensure GL setup is ready. Aspect ratio captured at
|
|
79
90
|
// load time so the composite shader can center-crop into the output.
|
|
@@ -116,13 +127,19 @@ private class BackgroundImageProcessor(
|
|
|
116
127
|
)
|
|
117
128
|
return null
|
|
118
129
|
}
|
|
119
|
-
val
|
|
120
|
-
val
|
|
121
|
-
if (
|
|
122
|
-
Log.w(TAG, "Degenerate dims ${
|
|
130
|
+
val bufW = inputBuffer.width
|
|
131
|
+
val bufH = inputBuffer.height
|
|
132
|
+
if (bufW <= 0 || bufH <= 0) {
|
|
133
|
+
Log.w(TAG, "Degenerate dims ${bufW}x${bufH}; forwarding.")
|
|
123
134
|
return null
|
|
124
135
|
}
|
|
125
136
|
|
|
137
|
+
// Ingest normalization: the OES->2D pass lands a DISPLAY-UPRIGHT frame, so
|
|
138
|
+
// the original FBO and output are sized in DISPLAY dims, the cover-fit runs
|
|
139
|
+
// against the display aspect, and the frame goes out rotation 0.
|
|
140
|
+
val width = Ingest.displayWidth(bufW, bufH, frame.rotation)
|
|
141
|
+
val height = Ingest.displayHeight(bufW, bufH, frame.rotation)
|
|
142
|
+
|
|
126
143
|
GlDebug.check("bgImage entry")
|
|
127
144
|
val saved = Egl.save()
|
|
128
145
|
var outputTextureId = 0
|
|
@@ -145,7 +162,8 @@ private class BackgroundImageProcessor(
|
|
|
145
162
|
origFbo.bind()
|
|
146
163
|
oes.use()
|
|
147
164
|
oes.setInt("uTex", 0)
|
|
148
|
-
|
|
165
|
+
// Compose transformMatrix with the display rotation so the FBO lands upright.
|
|
166
|
+
val texMatrix = Ingest.composedTexMatrix(inputBuffer.transformMatrix, frame.rotation)
|
|
149
167
|
GLES30.glUniformMatrix4fv(oes.uniformLocation("uTexMatrix"), 1, false, texMatrix, 0)
|
|
150
168
|
GLES30.glDisable(GLES30.GL_DEPTH_TEST)
|
|
151
169
|
GLES30.glDisable(GLES30.GL_BLEND)
|
|
@@ -208,8 +226,6 @@ private class BackgroundImageProcessor(
|
|
|
208
226
|
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
|
|
209
227
|
GlDebug.check("bgImage composite pass")
|
|
210
228
|
|
|
211
|
-
GLES30.glFinish()
|
|
212
|
-
|
|
213
229
|
GLES30.glFramebufferTexture2D(
|
|
214
230
|
GLES30.GL_FRAMEBUFFER,
|
|
215
231
|
GLES30.GL_COLOR_ATTACHMENT0,
|
|
@@ -222,28 +238,42 @@ private class BackgroundImageProcessor(
|
|
|
222
238
|
outputFboHandle = 0
|
|
223
239
|
GlDebug.check("bgImage output cleanup")
|
|
224
240
|
|
|
241
|
+
// R3: fence this frame and hand the previous GPU-complete frame off; the
|
|
242
|
+
// pipeline now owns outputTextureId, so zero the local handle.
|
|
243
|
+
val ready = pipeline.enqueue(
|
|
244
|
+
outputTextureId,
|
|
245
|
+
width,
|
|
246
|
+
height,
|
|
247
|
+
// Pixels are already display-upright after ingest; emit rotation 0.
|
|
248
|
+
0,
|
|
249
|
+
frame.timestampNs,
|
|
250
|
+
EffectTuning.debugTiming,
|
|
251
|
+
TAG,
|
|
252
|
+
)
|
|
253
|
+
outputTextureId = 0
|
|
254
|
+
ready ?: return null
|
|
255
|
+
|
|
225
256
|
val yc = yuvConverter ?: run {
|
|
226
257
|
val c = YuvConverter()
|
|
227
258
|
yuvConverter = c
|
|
228
259
|
c
|
|
229
260
|
}
|
|
230
261
|
|
|
231
|
-
val
|
|
262
|
+
val readyTextureId = ready.textureId
|
|
232
263
|
val outputBuffer = TextureBufferImpl(
|
|
233
|
-
width,
|
|
234
|
-
height,
|
|
264
|
+
ready.width,
|
|
265
|
+
ready.height,
|
|
235
266
|
VideoFrame.TextureBuffer.Type.RGB,
|
|
236
|
-
|
|
267
|
+
readyTextureId,
|
|
237
268
|
Matrix(),
|
|
238
269
|
textureHelper.handler,
|
|
239
270
|
yc,
|
|
240
271
|
Runnable {
|
|
241
|
-
GLES30.glDeleteTextures(1, intArrayOf(
|
|
272
|
+
GLES30.glDeleteTextures(1, intArrayOf(readyTextureId), 0)
|
|
242
273
|
},
|
|
243
274
|
)
|
|
244
|
-
outputTextureId = 0
|
|
245
275
|
|
|
246
|
-
VideoFrame(outputBuffer,
|
|
276
|
+
VideoFrame(outputBuffer, ready.rotation, ready.timestampNs)
|
|
247
277
|
} catch (t: Throwable) {
|
|
248
278
|
if (outputTextureId != 0) {
|
|
249
279
|
try {
|
|
@@ -288,15 +318,15 @@ private class BackgroundImageProcessor(
|
|
|
288
318
|
private fun ensureBackgroundTexture() {
|
|
289
319
|
if (backgroundTextureId != 0) return
|
|
290
320
|
val bmp = try {
|
|
291
|
-
context.assets.open("backgrounds/$assetName.
|
|
321
|
+
context.assets.open("backgrounds/$assetName.webp").use { stream ->
|
|
292
322
|
BitmapFactory.decodeStream(stream)
|
|
293
323
|
}
|
|
294
324
|
} catch (t: Throwable) {
|
|
295
|
-
Log.e(TAG, "failed to load asset backgrounds/$assetName.
|
|
325
|
+
Log.e(TAG, "failed to load asset backgrounds/$assetName.webp", t)
|
|
296
326
|
return
|
|
297
327
|
}
|
|
298
328
|
if (bmp == null) {
|
|
299
|
-
Log.e(TAG, "BitmapFactory returned null for backgrounds/$assetName.
|
|
329
|
+
Log.e(TAG, "BitmapFactory returned null for backgrounds/$assetName.webp")
|
|
300
330
|
return
|
|
301
331
|
}
|
|
302
332
|
// Pre-flip vertically so the texture lands in the shared convention
|