react-native-webrtc-kaleidoscope 1.1.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 +39 -18
- package/android/build.gradle +3 -4
- 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 +10 -0
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/ShadersGenerated.kt +15 -3
- 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/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/{office-1.web.js → dark-office.web.js} +3 -3
- 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/{office-1.js → home-dark.js} +3 -3
- 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/{office-2.js → home-light.js} +3 -3
- 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.map +1 -1
- package/dist/backgrounds/index.js +1 -1
- package/dist/backgrounds/index.js.map +1 -1
- 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/{office-2.web.js → light-office.web.js} +3 -3
- 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/presets.d.ts +1 -1
- package/dist/backgrounds/presets.d.ts.map +1 -1
- package/dist/backgrounds/presets.js +22 -9
- package/dist/backgrounds/presets.js.map +1 -1
- 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 +54 -27
- package/dist/index.js.map +1 -1
- package/dist/index.web.d.ts +14 -2
- package/dist/index.web.d.ts.map +1 -1
- package/dist/index.web.js +25 -9
- package/dist/index.web.js.map +1 -1
- 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 +163 -65
- 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/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.map +1 -1
- package/dist/web/shaders.generated.d.ts +1 -1
- package/dist/web/shaders.generated.d.ts.map +1 -1
- package/dist/web/shaders.generated.js +3 -3
- package/dist/web/shaders.generated.js.map +1 -1
- package/dist/web/shaders.js +1 -1
- 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 +13 -3
- package/ios/KaleidoscopeModule/EffectTuning.swift +47 -7
- package/ios/KaleidoscopeModule/KaleidoscopeModule.swift +12 -0
- package/ios/KaleidoscopeModule/Registration.swift +49 -6
- package/ios/KaleidoscopeModule/effects/BackgroundImageProcessor.swift +100 -33
- package/ios/KaleidoscopeModule/effects/BlurProcessor.swift +91 -31
- package/ios/KaleidoscopeModule/effects/FrameBridge.swift +11 -5
- package/ios/KaleidoscopeModule/effects/TransformProcessor.swift +173 -0
- package/ios/KaleidoscopeModule/gpu/Ingest.swift +169 -0
- package/ios/KaleidoscopeModule/gpu/MetalRenderer.swift +234 -56
- package/ios/KaleidoscopeModule/gpu/Orientation.swift +83 -0
- package/ios/KaleidoscopeModule/gpu/TextureBridge.swift +183 -12
- 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/Segmenter.swift +379 -56
- package/ios/KaleidoscopeModule/shaders/SHADERS.txt +1 -0
- package/ios/KaleidoscopeModule/shaders/blur.metalsrc +2 -2
- package/ios/KaleidoscopeModule/shaders/transform.metalsrc +22 -0
- package/package.json +93 -19
- package/src/backgrounds/README.md +15 -10
- package/src/backgrounds/dark-office.ts +6 -0
- package/src/backgrounds/{office-2.web.ts → dark-office.web.ts} +2 -2
- 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/{office-1.ts → home-dark.ts} +2 -2
- package/src/backgrounds/home-dark.web.ts +7 -0
- package/src/backgrounds/home-dark.webp +0 -0
- package/src/backgrounds/{office-2.ts → home-light.ts} +2 -2
- package/src/backgrounds/home-light.web.ts +7 -0
- package/src/backgrounds/home-light.webp +0 -0
- package/src/backgrounds/index.ts +1 -1
- package/src/backgrounds/light-office.ts +6 -0
- package/src/backgrounds/{office-1.web.ts → light-office.web.ts} +2 -2
- 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/presets.ts +22 -9
- 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 +64 -27
- package/src/index.web.ts +29 -11
- 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 +206 -67
- package/src/web/effects/transform.ts +69 -0
- package/src/web/segmenter.ts +34 -0
- package/src/web/shaders.generated.ts +3 -3
- package/src/web/shaders.ts +1 -1
- 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/office-1.d.ts +0 -3
- package/dist/backgrounds/office-1.d.ts.map +0 -1
- package/dist/backgrounds/office-1.js.map +0 -1
- package/dist/backgrounds/office-1.web.d.ts +0 -3
- package/dist/backgrounds/office-1.web.d.ts.map +0 -1
- package/dist/backgrounds/office-1.web.js.map +0 -1
- package/dist/backgrounds/office-1.webp +0 -0
- package/dist/backgrounds/office-2.d.ts +0 -3
- package/dist/backgrounds/office-2.d.ts.map +0 -1
- package/dist/backgrounds/office-2.js.map +0 -1
- package/dist/backgrounds/office-2.web.d.ts +0 -3
- package/dist/backgrounds/office-2.web.d.ts.map +0 -1
- package/dist/backgrounds/office-2.web.js.map +0 -1
- package/dist/backgrounds/office-2.webp +0 -0
- 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 -136
- package/ios/KaleidoscopeModule/resources/backgrounds/office-1.png +0 -0
- package/ios/KaleidoscopeModule/resources/backgrounds/office-2.png +0 -0
- package/src/backgrounds/office-1.webp +0 -0
- package/src/backgrounds/office-2.webp +0 -0
- 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,9 +8,11 @@
|
|
|
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
|
|
|
@@ -18,17 +20,17 @@
|
|
|
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 +
|
|
31
|
+
| Android (API 24+) | ✓ | ✓ | ✓ | OpenGL ES 3.0 + MediaPipe Selfie Segmentation (Tasks) |
|
|
30
32
|
| 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
|
|
33
|
+
| Safari / Firefox | — | — | — | No Insertable Streams; `applyVideoEffects` throws a clear capability error |
|
|
32
34
|
|
|
33
35
|
### Coming soon
|
|
34
36
|
|
|
@@ -101,31 +103,50 @@ import {
|
|
|
101
103
|
setMaskHardness,
|
|
102
104
|
setMaskThreshold,
|
|
103
105
|
} from 'react-native-webrtc-kaleidoscope';
|
|
106
|
+
import { darkOffice } from 'react-native-webrtc-kaleidoscope/backgrounds/dark-office';
|
|
104
107
|
|
|
105
108
|
const stream = await mediaDevices.getUserMedia({ video: true });
|
|
106
109
|
const [track] = stream.getVideoTracks();
|
|
107
110
|
|
|
108
|
-
applyVideoEffects(track, ['
|
|
111
|
+
applyVideoEffects(track, ['flip-x']); // also flip-y, rotate-cw, rotate-ccw
|
|
109
112
|
applyVideoEffects(track, ['blur']);
|
|
110
|
-
applyVideoEffects(track, [{ name: 'background-image', source:
|
|
113
|
+
applyVideoEffects(track, [{ name: 'background-image', source: darkOffice }]);
|
|
111
114
|
applyVideoEffects(track, []); // clear all effects
|
|
112
115
|
|
|
113
116
|
// Runtime tuning (effects pick up the new values on the next frame):
|
|
114
|
-
setBlurSigma(
|
|
115
|
-
setMaskHardness(0.
|
|
116
|
-
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.
|
|
117
120
|
```
|
|
118
121
|
|
|
119
122
|
Effects chain in array order.
|
|
120
123
|
|
|
121
|
-
**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:
|
|
122
125
|
|
|
123
126
|
| Platform | Blur sigma | Mask hardness | Mask threshold |
|
|
124
127
|
|---|---|---|---|
|
|
125
128
|
| Web (MediaPipe) | 25 | 0.2 | 0.85 |
|
|
126
|
-
| Android (
|
|
129
|
+
| Android (MediaPipe) | 30 | 0.2 | 0.6 |
|
|
127
130
|
|
|
128
|
-
The library ships
|
|
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.
|
|
129
150
|
|
|
130
151
|
## Web and native differences
|
|
131
152
|
|
|
@@ -133,9 +154,9 @@ The API surface is the same across platforms, but the runtimes differ in ways wo
|
|
|
133
154
|
|
|
134
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.
|
|
135
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.
|
|
136
|
-
- **Background presets ship as tree-shakeable files.**
|
|
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).
|
|
137
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.
|
|
138
|
-
- **Browser support on web.** Effects use Insertable Streams (`MediaStreamTrackProcessor` and `MediaStreamTrackGenerator`), which ship in Chromium-based browsers; Safari and Firefox
|
|
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.
|
|
139
160
|
|
|
140
161
|
## What this isn't
|
|
141
162
|
|
|
@@ -150,8 +171,8 @@ The codebase lives across four surfaces:
|
|
|
150
171
|
|
|
151
172
|
- `src/` — JS facade and shared types. `applyVideoEffects(track, effects)` plus runtime tuning setters.
|
|
152
173
|
- `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.
|
|
154
|
-
- `ios/` — Metal pipeline (Swift) with
|
|
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.
|
|
155
176
|
|
|
156
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.
|
|
157
178
|
|
package/android/build.gradle
CHANGED
|
@@ -55,10 +55,9 @@ dependencies {
|
|
|
55
55
|
}
|
|
56
56
|
compileOnly project(rnWebrtcProject)
|
|
57
57
|
|
|
58
|
-
// Person/background mask
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
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'
|
|
62
61
|
}
|
|
63
62
|
|
|
64
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
|
-
// 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
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
|