Welcome to mirror list, hosted at ThFree Co, Russian Federation.

JitsiStreamBackgroundEffect.worker.js « virtual-background « effects « media « utils « src - github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 787d4b4c5e2416da8ee897e3b49b08b529e99080 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import createTFLiteModule from './vendor/tflite/tflite'
import createTFLiteSIMDModule from './vendor/tflite/tflite-simd'
import withoutSIMD from './vendor/tflite/tflite.wasm'
import withSIMD from './vendor/tflite/tflite-simd.wasm'
import landscape from './vendor/models/selfie_segmentation_landscape.tflite'

const models = {
	modelLandscape: landscape.split('/').pop(),
}

self.compiled = false

self.onmessage = (e) => {
	const message = e.data.message
	switch (message) {
	case 'makeTFLite':
		self.segmentationPixelCount = e.data.segmentationPixelCount
		makeTFLite(e.data.simd)
		break
	case 'resizeSource':
		if (!self.compiled) return
		resizeSource(e.data.imageData, e.data.frameId)
		break
	case 'runInference':
		runInference()
		break
	default:
		console.error('JitsiStreamBackgroundEffect.worker: Message unknown.')
		console.error(message)
		break
	}
}

/**
 * @param {boolean} isSimd whether WebAssembly SIMD is available or not
 */
async function makeTFLite(isSimd) {
	try {
		switch (isSimd) {
		case true:
			self.wasmUrl = withSIMD.split('/').pop()
			self.tflite = await createTFLiteSIMDModule({ locateFile: (path) => { return self.wasmUrl } })
			break
		case false:
			self.wasmUrl = withoutSIMD.split('/').pop()
			self.tflite = await createTFLiteModule({ locateFile: (path) => { return self.wasmUrl } })
			break
		default:
			return
		}
		self.modelBufferOffset = self.tflite._getModelBufferMemoryOffset()
		self.modelResponse = await fetch(models.modelLandscape)

		if (!self.modelResponse.ok) {
			throw new Error('Failed to download tflite model!')
		}
		self.model = await self.modelResponse.arrayBuffer()

		self.tflite.HEAPU8.set(new Uint8Array(self.model), self.modelBufferOffset)

		await self.tflite._loadModel(self.model.byteLength)

		// Even if the wrong tflite file is downloaded (for example, if an HTML
		// error is downloaded instead of the file) loading the model will
		// succeed. However, if the model does not have certain values it could
		// be assumed that the model failed to load.
		if (!self.tflite._getInputWidth() || !self.tflite._getInputHeight()
			|| !self.tflite._getOutputWidth() || !self.tflite._getOutputHeight()) {
			throw new Error('Failed to load tflite model!')
		}

		self.compiled = true

		self.postMessage({ message: 'loaded' })

	} catch (error) {
		console.error(error)
		console.error('JitsiStreamBackgroundEffect.worker: tflite compilation failed. The web server may not be properly configured to send wasm and/or tflite files.')

		self.postMessage({ message: 'loadFailed' })
	}
}

/**
 * @param {ImageData} imageData the image data from the canvas
 * @param {number} frameId the ID of the frame that the image data belongs to
 */
function resizeSource(imageData, frameId) {
	const inputMemoryOffset = self.tflite._getInputMemoryOffset() / 4
	for (let i = 0; i < self.segmentationPixelCount; i++) {
		self.tflite.HEAPF32[inputMemoryOffset + (i * 3)] = imageData.data[i * 4] / 255
		self.tflite.HEAPF32[inputMemoryOffset + (i * 3) + 1] = imageData.data[(i * 4) + 1] / 255
		self.tflite.HEAPF32[inputMemoryOffset + (i * 3) + 2] = imageData.data[(i * 4) + 2] / 255
	}
	runInference(frameId)
}

/**
 * @param {number} frameId the ID of the frame that the image data belongs to
 */
function runInference(frameId) {
	self.tflite._runInference()
	const outputMemoryOffset = self.tflite._getOutputMemoryOffset() / 4
	const segmentationMaskData = []
	// All consts in Worker in obj array.
	for (let i = 0; i < self.segmentationPixelCount; i++) {

		const person = self.tflite.HEAPF32[outputMemoryOffset + i]

		segmentationMaskData.push({
			person,
		})
	}
	self.postMessage({ message: 'inferenceRun', segmentationResult: segmentationMaskData, frameId })
}

// This is needed to make the linter happy, but even if nothing is actually
// exported the worker is loaded as expected.
export default null