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
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// 1. Render the input OES camera texture through an OES->2D passthrough
|
|
5
5
|
// shader into a cached intermediate FBO (the "original 2D" copy).
|
|
6
6
|
// 2. Produce a mask via Mask.produce (which downsamples the original 2D,
|
|
7
|
-
// reads it back to a Bitmap, runs
|
|
8
|
-
// the confidence map as a 2D GL texture).
|
|
7
|
+
// reads it back to a Bitmap, runs MediaPipe segmentation off-thread via
|
|
8
|
+
// SegmentationEngine, uploads the confidence map as a 2D GL texture).
|
|
9
9
|
// 3. Run two separable Gaussian blur passes on the "original 2D" copy
|
|
10
10
|
// using ping-pong FBOs.
|
|
11
11
|
// 4. Composite original + blurred + mask into a fresh output texture via
|
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
// Every observable failure logs to adb logcat under Kaleidoscope.Blur and
|
|
17
17
|
// returns null so upstream forwards the original frame instead of crashing.
|
|
18
18
|
//
|
|
19
|
-
// Replaces the CPU implementation (manual Kotlin YUV/ARGB
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// cache, so the render thread is
|
|
19
|
+
// Replaces the original CPU implementation (manual Kotlin YUV/ARGB
|
|
20
|
+
// conversion). The CPU path was ~5-10 FPS at 720p; the GPU path now runs
|
|
21
|
+
// MediaPipe segmentation on a shared worker thread (see Mask.kt and
|
|
22
|
+
// SegmentationEngine.kt) with a last-known-mask cache, so the render thread is
|
|
23
|
+
// no longer gated on segmentation.
|
|
23
24
|
|
|
24
25
|
package com.simiancraft.kaleidoscope.effects
|
|
25
26
|
|
|
@@ -32,7 +33,9 @@ import com.oney.WebRTCModule.videoEffects.VideoFrameProcessorFactoryInterface
|
|
|
32
33
|
import com.simiancraft.kaleidoscope.EffectTuning
|
|
33
34
|
import com.simiancraft.kaleidoscope.gpu.Egl
|
|
34
35
|
import com.simiancraft.kaleidoscope.gpu.Fbo
|
|
36
|
+
import com.simiancraft.kaleidoscope.gpu.FramePipeline
|
|
35
37
|
import com.simiancraft.kaleidoscope.gpu.GlDebug
|
|
38
|
+
import com.simiancraft.kaleidoscope.gpu.Ingest
|
|
36
39
|
import com.simiancraft.kaleidoscope.gpu.GlProgram
|
|
37
40
|
import com.simiancraft.kaleidoscope.gpu.Shaders
|
|
38
41
|
import com.simiancraft.kaleidoscope.segmentation.Mask
|
|
@@ -54,10 +57,14 @@ import org.webrtc.YuvConverter
|
|
|
54
57
|
class BlurFactory(
|
|
55
58
|
private val context: Context,
|
|
56
59
|
) : VideoFrameProcessorFactoryInterface {
|
|
57
|
-
override fun build(): VideoFrameProcessor = BlurProcessor()
|
|
60
|
+
override fun build(): VideoFrameProcessor = BlurProcessor(context)
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
private class BlurProcessor : VideoFrameProcessor {
|
|
63
|
+
private class BlurProcessor(private val context: Context) : VideoFrameProcessor {
|
|
64
|
+
// process() is only ever invoked on the single SurfaceTextureHelper capture
|
|
65
|
+
// thread (VideoEffectProcessor.onFrameCaptured), so this never actually
|
|
66
|
+
// contends. Retained as cheap uncontended insurance and as an explicit marker
|
|
67
|
+
// that the GL state below is single-threaded; it is NOT a cross-thread guard.
|
|
61
68
|
private val lock = Any()
|
|
62
69
|
|
|
63
70
|
private var oesToTwoD: GlProgram? = null
|
|
@@ -71,31 +78,46 @@ private class BlurProcessor : VideoFrameProcessor {
|
|
|
71
78
|
private var blurBFbo: Fbo? = null
|
|
72
79
|
private var cachedWidth = 0
|
|
73
80
|
private var cachedHeight = 0
|
|
81
|
+
// R1: the blur ping-pong runs at a downscaled resolution; these hold its
|
|
82
|
+
// dimensions so the blur passes set uAxis in the downscaled texel space.
|
|
83
|
+
private var blurW = 0
|
|
84
|
+
private var blurH = 0
|
|
74
85
|
|
|
75
|
-
private val mask = Mask()
|
|
86
|
+
private val mask = Mask(context)
|
|
76
87
|
private var yuvConverter: YuvConverter? = null
|
|
77
88
|
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
|
|
89
|
+
// R3: one-frame GPU pipeline. process() hands each rendered texture here and
|
|
90
|
+
// gets the previous frame's GPU-complete texture back, so the capture thread
|
|
91
|
+
// never blocks on the current frame's GPU work (the old per-frame glFinish).
|
|
92
|
+
private val pipeline = FramePipeline()
|
|
93
|
+
|
|
94
|
+
// Linear-sampled separable Gaussian: 5 entries (center + 4 bilinear pairs).
|
|
95
|
+
// sigma comes from EffectTuning at frame time; the kernel is rebuilt on the
|
|
96
|
+
// CPU only when sigma changes. See src/web/blur-kernel.ts for the derivation.
|
|
82
97
|
private val blurWeights = FloatArray(KERNEL_TAPS)
|
|
83
98
|
private val blurOffsets = FloatArray(KERNEL_TAPS)
|
|
84
99
|
private var cachedKernelSigma: Float = Float.NaN
|
|
85
100
|
|
|
86
101
|
private fun ensureKernel(sigma: Float) {
|
|
87
102
|
if (sigma == cachedKernelSigma) return
|
|
88
|
-
val
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Normalize: center contributes once, each side tap contributes twice
|
|
96
|
-
// because the shader samples vUv +/- offset and adds each.
|
|
103
|
+
val s = sigma.toDouble()
|
|
104
|
+
fun g(t: Double) = Math.exp(-(t * t) / (2.0 * s * s))
|
|
105
|
+
// Linear-sampled: center + 4 bilinear pairs of dense texels (1,2)(3,4)
|
|
106
|
+
// (5,6)(7,8); each pair is one fractional-offset fetch. Normalize so
|
|
107
|
+
// center + 2*sum(pairs) == 1 (the shader samples vUv +/- offset, adds each).
|
|
108
|
+
blurOffsets[0] = 0f
|
|
109
|
+
blurWeights[0] = g(0.0).toFloat()
|
|
97
110
|
var sum = blurWeights[0]
|
|
98
|
-
for (
|
|
111
|
+
for (p in 1 until KERNEL_TAPS) {
|
|
112
|
+
val a = (2 * p - 1).toDouble()
|
|
113
|
+
val b = (2 * p).toDouble()
|
|
114
|
+
val wa = g(a)
|
|
115
|
+
val wb = g(b)
|
|
116
|
+
val w = wa + wb
|
|
117
|
+
blurOffsets[p] = ((a * wa + b * wb) / w).toFloat()
|
|
118
|
+
blurWeights[p] = w.toFloat()
|
|
119
|
+
sum += 2f * blurWeights[p]
|
|
120
|
+
}
|
|
99
121
|
for (i in 0 until KERNEL_TAPS) blurWeights[i] = blurWeights[i] / sum
|
|
100
122
|
cachedKernelSigma = sigma
|
|
101
123
|
}
|
|
@@ -136,13 +158,19 @@ private class BlurProcessor : VideoFrameProcessor {
|
|
|
136
158
|
Log.w(TAG, "TextureBuffer type is ${inputBuffer.type}; expected OES. Forwarding original.")
|
|
137
159
|
return null
|
|
138
160
|
}
|
|
139
|
-
val
|
|
140
|
-
val
|
|
141
|
-
if (
|
|
142
|
-
Log.w(TAG, "Degenerate dims ${
|
|
161
|
+
val bufW = inputBuffer.width
|
|
162
|
+
val bufH = inputBuffer.height
|
|
163
|
+
if (bufW <= 0 || bufH <= 0) {
|
|
164
|
+
Log.w(TAG, "Degenerate dims ${bufW}x${bufH}; forwarding.")
|
|
143
165
|
return null
|
|
144
166
|
}
|
|
145
167
|
|
|
168
|
+
// Ingest normalization: the OES->2D pass below lands a DISPLAY-UPRIGHT frame
|
|
169
|
+
// (Ingest folds frame.rotation into the texture matrix), so every cached FBO
|
|
170
|
+
// and the output are sized in DISPLAY dims, and the frame goes out rotation 0.
|
|
171
|
+
val width = Ingest.displayWidth(bufW, bufH, frame.rotation)
|
|
172
|
+
val height = Ingest.displayHeight(bufW, bufH, frame.rotation)
|
|
173
|
+
|
|
146
174
|
GlDebug.check("blur entry")
|
|
147
175
|
val saved = Egl.save()
|
|
148
176
|
var outputTextureId = 0
|
|
@@ -166,7 +194,8 @@ private class BlurProcessor : VideoFrameProcessor {
|
|
|
166
194
|
origFbo.bind()
|
|
167
195
|
oes.use()
|
|
168
196
|
oes.setInt("uTex", 0)
|
|
169
|
-
|
|
197
|
+
// Compose transformMatrix with the display rotation so the FBO lands upright.
|
|
198
|
+
val texMatrix = Ingest.composedTexMatrix(inputBuffer.transformMatrix, frame.rotation)
|
|
170
199
|
GLES30.glUniformMatrix4fv(oes.uniformLocation("uTexMatrix"), 1, false, texMatrix, 0)
|
|
171
200
|
GLES30.glDisable(GLES30.GL_DEPTH_TEST)
|
|
172
201
|
GLES30.glDisable(GLES30.GL_BLEND)
|
|
@@ -180,7 +209,11 @@ private class BlurProcessor : VideoFrameProcessor {
|
|
|
180
209
|
return null
|
|
181
210
|
}
|
|
182
211
|
|
|
183
|
-
// ===== Pass
|
|
212
|
+
// ===== Pass 1b: downsample original -> blurA (box-average) =====
|
|
213
|
+
// uAxis=0 collapses the kernel to its center tap (weights sum to 1), so
|
|
214
|
+
// this is a plain bilinear box-average into the downscaled target. Both
|
|
215
|
+
// blur passes then run in downscaled space; sampling the full-res
|
|
216
|
+
// original with downscaled-spaced offsets serrates the blur (see web fix).
|
|
184
217
|
GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
|
|
185
218
|
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, origFbo.texture)
|
|
186
219
|
blurA.bind()
|
|
@@ -188,19 +221,27 @@ private class BlurProcessor : VideoFrameProcessor {
|
|
|
188
221
|
blur.setInt("uTex", 0)
|
|
189
222
|
GLES30.glUniform1fv(blur.uniformLocation("uWeights"), KERNEL_TAPS, blurWeights, 0)
|
|
190
223
|
GLES30.glUniform1fv(blur.uniformLocation("uOffsets"), KERNEL_TAPS, blurOffsets, 0)
|
|
191
|
-
GLES30.glUniform2f(blur.uniformLocation("uAxis"),
|
|
224
|
+
GLES30.glUniform2f(blur.uniformLocation("uAxis"), 0.0f, 0.0f)
|
|
192
225
|
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
|
|
193
|
-
GlDebug.check("blur
|
|
226
|
+
GlDebug.check("blur downsample pass")
|
|
194
227
|
|
|
195
|
-
// ===== Pass
|
|
228
|
+
// ===== Pass 2: horizontal blur blurA -> blurB (downscaled) =====
|
|
196
229
|
GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
|
|
197
230
|
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, blurA.texture)
|
|
198
231
|
blurB.bind()
|
|
199
232
|
blur.use()
|
|
200
233
|
blur.setInt("uTex", 0)
|
|
201
|
-
GLES30.
|
|
202
|
-
GLES30.
|
|
203
|
-
|
|
234
|
+
GLES30.glUniform2f(blur.uniformLocation("uAxis"), 1.0f / blurW, 0.0f)
|
|
235
|
+
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
|
|
236
|
+
GlDebug.check("blur horizontal pass")
|
|
237
|
+
|
|
238
|
+
// ===== Pass 3: vertical blur blurB -> blurA (downscaled) =====
|
|
239
|
+
GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
|
|
240
|
+
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, blurB.texture)
|
|
241
|
+
blurA.bind()
|
|
242
|
+
blur.use()
|
|
243
|
+
blur.setInt("uTex", 0)
|
|
244
|
+
GLES30.glUniform2f(blur.uniformLocation("uAxis"), 0.0f, 1.0f / blurH)
|
|
204
245
|
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
|
|
205
246
|
GlDebug.check("blur vertical pass")
|
|
206
247
|
|
|
@@ -217,7 +258,8 @@ private class BlurProcessor : VideoFrameProcessor {
|
|
|
217
258
|
composite.setInt("uOriginal", 0)
|
|
218
259
|
|
|
219
260
|
GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
|
|
220
|
-
|
|
261
|
+
// Final blurred result is in blurA after the vertical pass.
|
|
262
|
+
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, blurA.texture)
|
|
221
263
|
composite.setInt("uBackground", 1)
|
|
222
264
|
|
|
223
265
|
GLES30.glActiveTexture(GLES30.GL_TEXTURE2)
|
|
@@ -239,10 +281,6 @@ private class BlurProcessor : VideoFrameProcessor {
|
|
|
239
281
|
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
|
|
240
282
|
GlDebug.check("blur composite pass")
|
|
241
283
|
|
|
242
|
-
// Synchronize before handing the output texture to the renderer's
|
|
243
|
-
// EGL context.
|
|
244
|
-
GLES30.glFinish()
|
|
245
|
-
|
|
246
284
|
// Detach the texture from the FBO and free the FBO. The texture lives
|
|
247
285
|
// with the VideoFrame and gets deleted in the release callback.
|
|
248
286
|
GLES30.glFramebufferTexture2D(
|
|
@@ -257,28 +295,45 @@ private class BlurProcessor : VideoFrameProcessor {
|
|
|
257
295
|
outputFboHandle = 0
|
|
258
296
|
GlDebug.check("blur output cleanup")
|
|
259
297
|
|
|
298
|
+
// R3: fence this frame's GPU work and hand the PREVIOUS (GPU-complete)
|
|
299
|
+
// frame's texture downstream instead of glFinish-ing on this one. The
|
|
300
|
+
// pipeline now owns outputTextureId, so zero our local handle to keep the
|
|
301
|
+
// orphan-cleanup catch from double-freeing it.
|
|
302
|
+
val ready = pipeline.enqueue(
|
|
303
|
+
outputTextureId,
|
|
304
|
+
width,
|
|
305
|
+
height,
|
|
306
|
+
// Pixels are already display-upright after ingest; emit rotation 0.
|
|
307
|
+
0,
|
|
308
|
+
frame.timestampNs,
|
|
309
|
+
EffectTuning.debugTiming,
|
|
310
|
+
TAG,
|
|
311
|
+
)
|
|
312
|
+
outputTextureId = 0
|
|
313
|
+
// First frame: nothing to hand off yet. Forward the original frame once.
|
|
314
|
+
ready ?: return null
|
|
315
|
+
|
|
260
316
|
val yc = yuvConverter ?: run {
|
|
261
317
|
val c = YuvConverter()
|
|
262
318
|
yuvConverter = c
|
|
263
319
|
c
|
|
264
320
|
}
|
|
265
321
|
|
|
266
|
-
val
|
|
322
|
+
val readyTextureId = ready.textureId
|
|
267
323
|
val outputBuffer = TextureBufferImpl(
|
|
268
|
-
width,
|
|
269
|
-
height,
|
|
324
|
+
ready.width,
|
|
325
|
+
ready.height,
|
|
270
326
|
VideoFrame.TextureBuffer.Type.RGB,
|
|
271
|
-
|
|
327
|
+
readyTextureId,
|
|
272
328
|
Matrix(),
|
|
273
329
|
textureHelper.handler,
|
|
274
330
|
yc,
|
|
275
331
|
Runnable {
|
|
276
|
-
GLES30.glDeleteTextures(1, intArrayOf(
|
|
332
|
+
GLES30.glDeleteTextures(1, intArrayOf(readyTextureId), 0)
|
|
277
333
|
},
|
|
278
334
|
)
|
|
279
|
-
outputTextureId = 0
|
|
280
335
|
|
|
281
|
-
VideoFrame(outputBuffer,
|
|
336
|
+
VideoFrame(outputBuffer, ready.rotation, ready.timestampNs)
|
|
282
337
|
} catch (t: Throwable) {
|
|
283
338
|
if (outputTextureId != 0) {
|
|
284
339
|
try {
|
|
@@ -320,9 +375,18 @@ private class BlurProcessor : VideoFrameProcessor {
|
|
|
320
375
|
originalFbo?.delete()
|
|
321
376
|
blurAFbo?.delete()
|
|
322
377
|
blurBFbo?.delete()
|
|
378
|
+
// R1: blur at quarter area (half each axis), floored so the short side
|
|
379
|
+
// stays >= 256px. The original stays full-res (foreground source + blur
|
|
380
|
+
// input); the composite upscales the downscaled blurred bg with GL_LINEAR
|
|
381
|
+
// for free, and the blur discards the detail the extra resolution carries.
|
|
382
|
+
val shortSide = minOf(width, height)
|
|
383
|
+
val target = maxOf(256, Math.round(shortSide * 0.5f))
|
|
384
|
+
val scale = target.toFloat() / shortSide
|
|
385
|
+
blurW = Math.round(width * scale).coerceAtLeast(2).let { if (it % 2 == 0) it else it - 1 }
|
|
386
|
+
blurH = Math.round(height * scale).coerceAtLeast(2).let { if (it % 2 == 0) it else it - 1 }
|
|
323
387
|
originalFbo = Fbo(width, height)
|
|
324
|
-
blurAFbo = Fbo(
|
|
325
|
-
blurBFbo = Fbo(
|
|
388
|
+
blurAFbo = Fbo(blurW, blurH)
|
|
389
|
+
blurBFbo = Fbo(blurW, blurH)
|
|
326
390
|
cachedWidth = width
|
|
327
391
|
cachedHeight = height
|
|
328
392
|
GlDebug.check("blur intermediates allocated")
|
|
@@ -331,6 +395,6 @@ private class BlurProcessor : VideoFrameProcessor {
|
|
|
331
395
|
companion object {
|
|
332
396
|
private const val TAG = "Kaleidoscope.Blur"
|
|
333
397
|
private const val GL_TEXTURE_EXTERNAL_OES = 0x8D65
|
|
334
|
-
private const val KERNEL_TAPS =
|
|
398
|
+
private const val KERNEL_TAPS = 5
|
|
335
399
|
}
|
|
336
400
|
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// Android transform effects — GPU pipeline. One factory class serves all four
|
|
2
|
+
// geometric reorientation ops (flip-x, flip-y, rotate-cw, rotate-ccw); each
|
|
3
|
+
// registration passes a different Orientation.Op.
|
|
4
|
+
//
|
|
5
|
+
// flip-x = screen-horizontal mirror (left<->right, head stays up); keeps w x h.
|
|
6
|
+
// flip-y = screen-vertical flip (upside down, left/right unchanged); keeps w x h.
|
|
7
|
+
// rotate-cw = whole frame rotated 90 degrees clockwise; output dims swap to h x w.
|
|
8
|
+
// rotate-ccw = 90 degrees counter-clockwise; output dims swap to h x w.
|
|
9
|
+
//
|
|
10
|
+
// flip-x replaces the old CPU "mirror" effect (the corrected screen-horizontal
|
|
11
|
+
// mirror); MirrorFactory and its "mirror" registration are removed.
|
|
12
|
+
//
|
|
13
|
+
// Per frame:
|
|
14
|
+
// 1. Render the input OES camera texture through the OES->2D passthrough into
|
|
15
|
+
// a cached DISPLAY-UPRIGHT "original 2D" FBO, exactly like BlurFactory /
|
|
16
|
+
// BackgroundImageFactory. Ingest folds the display rotation into the
|
|
17
|
+
// texture matrix, so the FBO is sized in display dims and already upright.
|
|
18
|
+
// 2. Single TRANSFORM_FRAG pass: sample that upright 2D copy through the
|
|
19
|
+
// uUvTransform mat2 from Orientation.mat2For(op) into a fresh output
|
|
20
|
+
// texture sized display w x h (flips) or h x w (rotations).
|
|
21
|
+
// 3. Wrap the fresh output texture in a TextureBufferImpl and return a
|
|
22
|
+
// VideoFrame at rotation 0 (pixels are already upright).
|
|
23
|
+
//
|
|
24
|
+
// Camera orientation lives ONLY in Ingest; the op here is pure screen space and
|
|
25
|
+
// never consults frame.rotation. All failure paths log under
|
|
26
|
+
// Kaleidoscope.Transform and return null so upstream forwards the original frame.
|
|
27
|
+
|
|
28
|
+
package com.simiancraft.kaleidoscope.effects
|
|
29
|
+
|
|
30
|
+
import android.graphics.Matrix
|
|
31
|
+
import android.opengl.GLES30
|
|
32
|
+
import android.util.Log
|
|
33
|
+
import com.oney.WebRTCModule.videoEffects.VideoFrameProcessor
|
|
34
|
+
import com.oney.WebRTCModule.videoEffects.VideoFrameProcessorFactoryInterface
|
|
35
|
+
import com.simiancraft.kaleidoscope.EffectTuning
|
|
36
|
+
import com.simiancraft.kaleidoscope.gpu.Egl
|
|
37
|
+
import com.simiancraft.kaleidoscope.gpu.Fbo
|
|
38
|
+
import com.simiancraft.kaleidoscope.gpu.FramePipeline
|
|
39
|
+
import com.simiancraft.kaleidoscope.gpu.GlDebug
|
|
40
|
+
import com.simiancraft.kaleidoscope.gpu.GlProgram
|
|
41
|
+
import com.simiancraft.kaleidoscope.gpu.Ingest
|
|
42
|
+
import com.simiancraft.kaleidoscope.gpu.Orientation
|
|
43
|
+
import com.simiancraft.kaleidoscope.gpu.Shaders
|
|
44
|
+
import org.webrtc.SurfaceTextureHelper
|
|
45
|
+
import org.webrtc.TextureBufferImpl
|
|
46
|
+
import org.webrtc.VideoFrame
|
|
47
|
+
import org.webrtc.YuvConverter
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param op Which screen-space reorientation this factory's processor applies.
|
|
51
|
+
*/
|
|
52
|
+
class TransformFactory(
|
|
53
|
+
private val op: Orientation.Op,
|
|
54
|
+
) : VideoFrameProcessorFactoryInterface {
|
|
55
|
+
override fun build(): VideoFrameProcessor = TransformProcessor(op)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private class TransformProcessor(
|
|
59
|
+
private val op: Orientation.Op,
|
|
60
|
+
) : VideoFrameProcessor {
|
|
61
|
+
// process() is only ever invoked on the single SurfaceTextureHelper capture
|
|
62
|
+
// thread (VideoEffectProcessor.onFrameCaptured), so this never actually
|
|
63
|
+
// contends. Retained as cheap uncontended insurance and as an explicit marker
|
|
64
|
+
// that the GL state below is single-threaded; it is NOT a cross-thread guard.
|
|
65
|
+
private val lock = Any()
|
|
66
|
+
|
|
67
|
+
private var oesToTwoD: GlProgram? = null
|
|
68
|
+
private var transformProgram: GlProgram? = null
|
|
69
|
+
|
|
70
|
+
// Cached display-oriented "original 2D" copy at full input resolution.
|
|
71
|
+
private var originalFbo: Fbo? = null
|
|
72
|
+
private var cachedWidth = 0
|
|
73
|
+
private var cachedHeight = 0
|
|
74
|
+
|
|
75
|
+
private var yuvConverter: YuvConverter? = null
|
|
76
|
+
|
|
77
|
+
// R3: one-frame GPU pipeline (see FramePipeline). Replaces the per-frame
|
|
78
|
+
// glFinish; only changes WHEN the frame is returned, not its orientation.
|
|
79
|
+
private val pipeline = FramePipeline()
|
|
80
|
+
|
|
81
|
+
override fun process(frame: VideoFrame, textureHelper: SurfaceTextureHelper?): VideoFrame? {
|
|
82
|
+
return synchronized(lock) { processOuter(frame, textureHelper) }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private fun processOuter(frame: VideoFrame, textureHelper: SurfaceTextureHelper?): VideoFrame? {
|
|
86
|
+
return try {
|
|
87
|
+
processInner(frame, textureHelper)
|
|
88
|
+
} catch (t: Throwable) {
|
|
89
|
+
Log.e(
|
|
90
|
+
TAG,
|
|
91
|
+
"process() threw; falling through to original frame. op=$op " +
|
|
92
|
+
"frame=${frame.buffer.width}x${frame.buffer.height} " +
|
|
93
|
+
"rotation=${frame.rotation} bufferClass=${frame.buffer.javaClass.simpleName}",
|
|
94
|
+
t,
|
|
95
|
+
)
|
|
96
|
+
null
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private fun processInner(
|
|
101
|
+
frame: VideoFrame,
|
|
102
|
+
textureHelper: SurfaceTextureHelper?,
|
|
103
|
+
): VideoFrame? {
|
|
104
|
+
if (textureHelper == null) {
|
|
105
|
+
Log.w(TAG, "textureHelper is null; falling through.")
|
|
106
|
+
return null
|
|
107
|
+
}
|
|
108
|
+
val inputBuffer = frame.buffer
|
|
109
|
+
if (inputBuffer !is VideoFrame.TextureBuffer) {
|
|
110
|
+
// Chained after a CPU effect emitting I420 — silently forward.
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
if (inputBuffer.type != VideoFrame.TextureBuffer.Type.OES) {
|
|
114
|
+
Log.w(TAG, "TextureBuffer type is ${inputBuffer.type}; expected OES. Forwarding original.")
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
val bufW = inputBuffer.width
|
|
118
|
+
val bufH = inputBuffer.height
|
|
119
|
+
if (bufW <= 0 || bufH <= 0) {
|
|
120
|
+
Log.w(TAG, "Degenerate dims ${bufW}x${bufH}; forwarding.")
|
|
121
|
+
return null
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Ingest normalization: the OES->2D pass lands a DISPLAY-UPRIGHT frame, so
|
|
125
|
+
// the original FBO is sized in DISPLAY dims and the op runs in pure screen
|
|
126
|
+
// space (Orientation no longer consults frame.rotation).
|
|
127
|
+
val width = Ingest.displayWidth(bufW, bufH, frame.rotation)
|
|
128
|
+
val height = Ingest.displayHeight(bufW, bufH, frame.rotation)
|
|
129
|
+
|
|
130
|
+
// Rotations swap the (display) output dimensions; flips keep them.
|
|
131
|
+
val swap = Orientation.swapsDimensions(op)
|
|
132
|
+
val outWidth = if (swap) height else width
|
|
133
|
+
val outHeight = if (swap) width else height
|
|
134
|
+
|
|
135
|
+
GlDebug.check("transform entry")
|
|
136
|
+
val saved = Egl.save()
|
|
137
|
+
var outputTextureId = 0
|
|
138
|
+
var outputFboHandle = 0
|
|
139
|
+
return try {
|
|
140
|
+
ensurePrograms()
|
|
141
|
+
ensureIntermediates(width, height)
|
|
142
|
+
val origFbo = originalFbo ?: error("originalFbo null after ensure")
|
|
143
|
+
val oes = oesToTwoD ?: error("oesToTwoD program null after ensure")
|
|
144
|
+
val transform = transformProgram ?: error("transformProgram null after ensure")
|
|
145
|
+
|
|
146
|
+
// ===== Pass 1: OES camera -> display-oriented "original 2D" =====
|
|
147
|
+
GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
|
|
148
|
+
GLES30.glBindTexture(GL_TEXTURE_EXTERNAL_OES, inputBuffer.textureId)
|
|
149
|
+
origFbo.bind()
|
|
150
|
+
oes.use()
|
|
151
|
+
oes.setInt("uTex", 0)
|
|
152
|
+
// Compose transformMatrix with the display rotation so the FBO lands upright.
|
|
153
|
+
val texMatrix = Ingest.composedTexMatrix(inputBuffer.transformMatrix, frame.rotation)
|
|
154
|
+
GLES30.glUniformMatrix4fv(oes.uniformLocation("uTexMatrix"), 1, false, texMatrix, 0)
|
|
155
|
+
GLES30.glDisable(GLES30.GL_DEPTH_TEST)
|
|
156
|
+
GLES30.glDisable(GLES30.GL_BLEND)
|
|
157
|
+
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
|
|
158
|
+
GlDebug.check("transform OES->2D")
|
|
159
|
+
|
|
160
|
+
// ===== Pass 2: geometric reorientation into a fresh output texture =====
|
|
161
|
+
// Output FBO is sized for the (possibly swapped) output dims. The
|
|
162
|
+
// uUvTransform mat2 reads the square [0,1] original UV back through the
|
|
163
|
+
// op; rotations map the unit square onto itself, so swapping the FBO's
|
|
164
|
+
// physical dims yields the rotated image with no UV clamping.
|
|
165
|
+
val outputFbo = Fbo(outWidth, outHeight)
|
|
166
|
+
outputTextureId = outputFbo.texture
|
|
167
|
+
outputFboHandle = outputFbo.framebuffer
|
|
168
|
+
|
|
169
|
+
outputFbo.bind()
|
|
170
|
+
transform.use()
|
|
171
|
+
GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
|
|
172
|
+
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, origFbo.texture)
|
|
173
|
+
transform.setInt("uTex", 0)
|
|
174
|
+
// Input is already display-upright (ingest), so the op is pure screen
|
|
175
|
+
// space; frame.rotation is no longer consulted here.
|
|
176
|
+
transform.setMat2("uUvTransform", Orientation.mat2For(op))
|
|
177
|
+
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
|
|
178
|
+
GlDebug.check("transform reorient pass")
|
|
179
|
+
|
|
180
|
+
// Detach the texture from the FBO and free the FBO; the texture lives
|
|
181
|
+
// with the VideoFrame and is deleted in the release callback.
|
|
182
|
+
GLES30.glFramebufferTexture2D(
|
|
183
|
+
GLES30.GL_FRAMEBUFFER,
|
|
184
|
+
GLES30.GL_COLOR_ATTACHMENT0,
|
|
185
|
+
GLES30.GL_TEXTURE_2D,
|
|
186
|
+
0,
|
|
187
|
+
0,
|
|
188
|
+
)
|
|
189
|
+
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
|
|
190
|
+
GLES30.glDeleteFramebuffers(1, intArrayOf(outputFboHandle), 0)
|
|
191
|
+
outputFboHandle = 0
|
|
192
|
+
GlDebug.check("transform output cleanup")
|
|
193
|
+
|
|
194
|
+
// R3: fence this frame and hand the previous GPU-complete frame off. The
|
|
195
|
+
// (possibly swapped) output dims travel with the frame, so the previous
|
|
196
|
+
// frame's own dims/rotation are wrapped unchanged; orientation behavior
|
|
197
|
+
// is untouched, only the return is deferred one frame.
|
|
198
|
+
val ready = pipeline.enqueue(
|
|
199
|
+
outputTextureId,
|
|
200
|
+
outWidth,
|
|
201
|
+
outHeight,
|
|
202
|
+
// Pixels are already display-upright after ingest; emit rotation 0.
|
|
203
|
+
0,
|
|
204
|
+
frame.timestampNs,
|
|
205
|
+
EffectTuning.debugTiming,
|
|
206
|
+
TAG,
|
|
207
|
+
)
|
|
208
|
+
outputTextureId = 0
|
|
209
|
+
ready ?: return null
|
|
210
|
+
|
|
211
|
+
val yc = yuvConverter ?: run {
|
|
212
|
+
val c = YuvConverter()
|
|
213
|
+
yuvConverter = c
|
|
214
|
+
c
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
val readyTextureId = ready.textureId
|
|
218
|
+
val outputBuffer = TextureBufferImpl(
|
|
219
|
+
ready.width,
|
|
220
|
+
ready.height,
|
|
221
|
+
VideoFrame.TextureBuffer.Type.RGB,
|
|
222
|
+
readyTextureId,
|
|
223
|
+
Matrix(),
|
|
224
|
+
textureHelper.handler,
|
|
225
|
+
yc,
|
|
226
|
+
Runnable {
|
|
227
|
+
GLES30.glDeleteTextures(1, intArrayOf(readyTextureId), 0)
|
|
228
|
+
},
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
VideoFrame(outputBuffer, ready.rotation, ready.timestampNs)
|
|
232
|
+
} catch (t: Throwable) {
|
|
233
|
+
if (outputTextureId != 0) {
|
|
234
|
+
try {
|
|
235
|
+
GLES30.glDeleteTextures(1, intArrayOf(outputTextureId), 0)
|
|
236
|
+
} catch (delErr: Throwable) {
|
|
237
|
+
Log.w(TAG, "failed to free orphan texture $outputTextureId", delErr)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (outputFboHandle != 0) {
|
|
241
|
+
try {
|
|
242
|
+
GLES30.glDeleteFramebuffers(1, intArrayOf(outputFboHandle), 0)
|
|
243
|
+
} catch (delErr: Throwable) {
|
|
244
|
+
Log.w(TAG, "failed to free orphan FBO $outputFboHandle", delErr)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
throw t
|
|
248
|
+
} finally {
|
|
249
|
+
Egl.restore(saved)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private fun ensurePrograms() {
|
|
254
|
+
if (oesToTwoD == null) {
|
|
255
|
+
oesToTwoD = GlProgram(Shaders.PASSTHROUGH_VERT, Shaders.OES_PASSTHROUGH_FRAG)
|
|
256
|
+
GlDebug.check("oesToTwoD program compile/link")
|
|
257
|
+
}
|
|
258
|
+
if (transformProgram == null) {
|
|
259
|
+
transformProgram = GlProgram(Shaders.PASSTHROUGH_VERT, Shaders.TRANSFORM_FRAG)
|
|
260
|
+
GlDebug.check("transform program compile/link")
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private fun ensureIntermediates(width: Int, height: Int) {
|
|
265
|
+
if (cachedWidth == width && cachedHeight == height && originalFbo != null) return
|
|
266
|
+
originalFbo?.delete()
|
|
267
|
+
originalFbo = Fbo(width, height)
|
|
268
|
+
cachedWidth = width
|
|
269
|
+
cachedHeight = height
|
|
270
|
+
GlDebug.check("transform intermediates allocated")
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
companion object {
|
|
274
|
+
private const val TAG = "Kaleidoscope.Transform"
|
|
275
|
+
private const val GL_TEXTURE_EXTERNAL_OES = 0x8D65
|
|
276
|
+
}
|
|
277
|
+
}
|