/*
Phaneron - Clustered, accelerated and cloud-fit video server, pre-assembled and in kit form.
Copyright (C) 2020 Streampunk Media Ltd.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
https://www.streampunk.media/ mailto:furnace@streampunk.media
14 Ormiscaig, Aultbea, Achnasheen, IV22 2JJ U.K.
*/
import { clContext as nodenCLContext, OpenCLBuffer, KernelParams } from 'nodencl'
import { ClJobs } from '../clJobQueue'
import ImageProcess from './imageProcess'
import Transform from './transform'
import Mix from './mix'
import Wipe from './wipe'
import Combine from './combine'
export default class Switch {
private readonly clContext: nodenCLContext
private readonly chanID: string
private readonly clJobs: ClJobs
private readonly width: number
private readonly height: number
private readonly numInputs: number
private readonly numOverlays: number
private xform0: ImageProcess | null = null
private xform1: ImageProcess | null = null
private rgbaXf0: OpenCLBuffer | null = null
private rgbaXf1: OpenCLBuffer | null = null
private rgbaMx: OpenCLBuffer | null = null
private mixer: ImageProcess | null = null
private wiper: ImageProcess | null = null
private combiner: ImageProcess | null = null
constructor(
clContext: nodenCLContext,
chanID: string,
clJobs: ClJobs,
width: number,
height: number,
numInputs: number,
numOverlays: number
) {
this.clContext = clContext
this.chanID = `${chanID} switch`
this.clJobs = clJobs
this.width = width
this.height = height
this.numInputs = numInputs
this.numOverlays = numOverlays
}
async init(): Promise {
const numBytesRGBA = this.width * this.height * 4 * 4
this.xform0 = new ImageProcess(
this.clContext,
new Transform(this.clContext, this.width, this.height),
this.clJobs
)
await this.xform0.init()
this.rgbaXf0 = await this.clContext.createBuffer(
numBytesRGBA,
'readwrite',
'coarse',
{
width: this.width,
height: this.height
},
'switch'
)
if (this.numInputs > 1) {
this.xform1 = new ImageProcess(
this.clContext,
new Transform(this.clContext, this.width, this.height),
this.clJobs
)
await this.xform1.init()
this.rgbaXf1 = await this.clContext.createBuffer(
numBytesRGBA,
'readwrite',
'coarse',
{
width: this.width,
height: this.height
},
'switch'
)
this.mixer = new ImageProcess(this.clContext, new Mix(this.width, this.height), this.clJobs)
await this.mixer.init()
this.wiper = new ImageProcess(this.clContext, new Wipe(this.width, this.height), this.clJobs)
await this.wiper.init()
}
this.combiner = new ImageProcess(
this.clContext,
new Combine(this.width, this.height, this.numOverlays),
this.clJobs
)
await this.combiner.init()
this.rgbaMx = await this.clContext.createBuffer(
numBytesRGBA,
'readwrite',
'coarse',
{
width: this.width,
height: this.height
},
'switch'
)
}
async processFrame(
inParams: Array,
mixParams: KernelParams,
overlays: Array,
output: OpenCLBuffer
): Promise {
if (
!(
this.xform0 &&
(this.numInputs === 1 || (this.xform1 && this.mixer && this.wiper)) &&
this.combiner
)
)
throw new Error(`Switch needs to be initialised ${this.numInputs}`)
inParams[0].output = this.rgbaXf0
const inBuf0 = inParams[0].input as OpenCLBuffer
await this.xform0.run(inParams[0], { source: this.chanID, timestamp: inBuf0.timestamp }, () =>
inBuf0.release()
)
if (this.numInputs > 1) {
inParams[1].output = this.rgbaXf1
const inBuf1 = inParams[1].input as OpenCLBuffer
await this.xform1?.run(
inParams[1],
{ source: this.chanID, timestamp: inBuf1.timestamp },
() => inBuf1.release()
)
if (mixParams.wipe) {
await this.wiper?.run(
{ input0: this.rgbaXf0, input1: this.rgbaXf1, wipe: mixParams.frac, output: this.rgbaMx },
{ source: this.chanID, timestamp: inBuf0.timestamp },
() => {
this.rgbaXf0?.release()
this.rgbaXf1?.release()
}
)
} else {
await this.mixer?.run(
{ input0: this.rgbaXf0, input1: this.rgbaXf1, mix: mixParams.frac, output: this.rgbaMx },
{ source: this.chanID, timestamp: inBuf0.timestamp },
() => {
this.rgbaXf0?.release()
this.rgbaXf1?.release()
}
)
}
} else {
this.rgbaMx = this.rgbaXf0
}
return await this.combiner.run(
{ bgIn: this.rgbaMx, ovIn: overlays, output: output },
{ source: this.chanID, timestamp: inBuf0.timestamp },
() => {
this.rgbaMx?.release()
overlays.forEach((o) => o.release())
}
)
}
}