-
-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Simplifying Output * Moved Values * Moved Visibility * Move outputwindows to outputhelper * Moved more code to bounds * Added LifeCycle * Fully replaced output with OutputHelper * Moved OutputHelper * Updated all code to reference Output object instead of outputWindows[] * Got previewWindow displaying * Locked preview to window * Got screens aligned and sticking in place * Fixed minimize/restore and add/remove outputs * Removed unused preview capture code * Re-enabled ndi and server outputs without beginFrameSubscription * Added CaptureHelper * Reorganized * Moved captures list to Output.captureOptions * Moved CaptureTransmitter * Code cleanup * Removed output windows * Got good performance out of preview renders * Previews look good except full screen * Fixed fullscreen * Mirrored preview outputs * Got preview videos playing smoothly * Fixed website scaling
- Loading branch information
Showing
24 changed files
with
990 additions
and
791 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import type { BrowserWindow, Display, NativeImage, Size } from "electron" | ||
import electron from "electron" | ||
import { NdiSender } from "../ndi/NdiSender" | ||
import { CaptureTransmitter } from "./helpers/CaptureTransmitter" | ||
import { CaptureOptions } from "./CaptureOptions" | ||
import { CaptureLifecycle } from "./helpers/CaptureLifecycle" | ||
import { OutputHelper } from "../output/OutputHelper" | ||
|
||
export class CaptureHelper { | ||
static Lifecycle = CaptureLifecycle | ||
static Transmitter = CaptureTransmitter | ||
|
||
private static framerates: any = { | ||
server: 30, | ||
unconnected: 1, | ||
connected: 30, | ||
} | ||
static customFramerates: any = {} | ||
|
||
static getDefaultCapture(window: BrowserWindow, id: string): CaptureOptions { | ||
let screen: Display = this.getWindowScreen(window) | ||
|
||
let defaultFramerates = { | ||
server: this.framerates.server, | ||
ndi: this.framerates.connected, | ||
} | ||
|
||
return { | ||
window, | ||
subscribed: false, | ||
displayFrequency: screen.displayFrequency || 60, | ||
options: { server: false, ndi: false }, | ||
framerates: defaultFramerates, | ||
id, | ||
} | ||
} | ||
|
||
// START | ||
|
||
static storedFrames: any = {} | ||
|
||
static updateFramerate(id: string) { | ||
const captureOptions = OutputHelper.getOutput(id)?.captureOptions | ||
if (!captureOptions) return | ||
|
||
if (NdiSender.NDI[id]) { | ||
let ndiFramerate = this.framerates.unconnected | ||
if (NdiSender.NDI[id].status === "connected") ndiFramerate = this.customFramerates[id]?.ndi || this.framerates.connected | ||
|
||
if (captureOptions.framerates.ndi !== parseInt(ndiFramerate)) { | ||
captureOptions.framerates.ndi = parseInt(ndiFramerate) | ||
CaptureTransmitter.startChannel(id, "ndi") | ||
} | ||
} | ||
} | ||
|
||
static getWindowScreen(window: BrowserWindow) { | ||
return electron.screen.getDisplayMatching({ | ||
x: window.getBounds().x, | ||
y: window.getBounds().y, | ||
width: window.getBounds().width, | ||
height: window.getBounds().height, | ||
}) | ||
} | ||
|
||
static resizeImage(image: NativeImage, initialSize: Size, newSize: Size) { | ||
if (initialSize.width / initialSize.height >= newSize.width / newSize.height) image = image.resize({ width: newSize.width }) | ||
else image = image.resize({ height: newSize.height }) | ||
|
||
return image | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { BrowserWindow } from "electron" | ||
|
||
export type CaptureOptions = { | ||
id: string | ||
window: BrowserWindow | ||
subscribed: boolean | ||
displayFrequency: number | ||
options: any | ||
framerates: any | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { NativeImage } from "electron" | ||
import { CaptureHelper } from "../CaptureHelper" | ||
import { OutputHelper } from "../../output/OutputHelper" | ||
|
||
export class CaptureLifecycle { | ||
static startCapture(id: string, toggle: any = {}) { | ||
const output = OutputHelper.getOutput(id) | ||
let window = output?.window | ||
let windowIsRemoved = !window || window.isDestroyed() | ||
if (windowIsRemoved) { | ||
delete output.captureOptions | ||
return | ||
} | ||
|
||
if (!output.captureOptions) output.captureOptions = CaptureHelper.getDefaultCapture(window, id) | ||
|
||
if (output.captureOptions) { | ||
const capture = output.captureOptions | ||
Object.keys(toggle).map((key) => { | ||
capture.options[key] = toggle[key] | ||
}) | ||
} | ||
|
||
CaptureHelper.updateFramerate(id) | ||
|
||
if (output.captureOptions.subscribed) return | ||
CaptureHelper.Transmitter.startTransmitting(id) | ||
output.captureOptions.subscribed = true | ||
|
||
cpuCapture() | ||
async function cpuCapture() { | ||
if (!output.captureOptions || output.captureOptions.window.isDestroyed()) return | ||
let image = await output.captureOptions.window.webContents.capturePage() | ||
processFrame(image) | ||
let frameRate = output.captureOptions.framerates.ndi | ||
if (output.captureOptions.framerates.server > frameRate) frameRate = output.captureOptions.framerates.server | ||
const ms = Math.round(1000 / frameRate) | ||
setTimeout(cpuCapture, ms) | ||
} | ||
|
||
function processFrame(image: NativeImage) { | ||
CaptureHelper.storedFrames[id] = image | ||
} | ||
} | ||
|
||
// STOP | ||
static stopAllCaptures() { | ||
OutputHelper.getAllOutputs().forEach((output) => { | ||
if (output[1].captureOptions) this.stopCapture(output[0]) | ||
}) | ||
} | ||
|
||
static stopCapture(id: string) { | ||
const output = OutputHelper.getOutput(id) | ||
const capture = output.captureOptions | ||
return new Promise((resolve) => { | ||
if (!capture) return resolve(true) | ||
CaptureHelper.Transmitter.removeAllChannels(id) | ||
let windowIsRemoved = !capture.window || capture.window.isDestroyed() | ||
if (windowIsRemoved) return deleteAndResolve() | ||
|
||
console.log("Capture - stopping: " + id) | ||
|
||
endSubscription() | ||
removeListeners() | ||
deleteAndResolve() | ||
|
||
function deleteAndResolve() { | ||
delete output.captureOptions | ||
resolve(true) | ||
} | ||
}) | ||
|
||
function endSubscription() { | ||
if (!capture?.subscribed) return | ||
|
||
capture.window.webContents.endFrameSubscription() | ||
capture.subscribed = false | ||
} | ||
|
||
function removeListeners() { | ||
capture?.window.removeAllListeners() | ||
capture?.window.webContents.removeAllListeners() | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.