camera-trng/scripts/obs-noise-camera.mjs

219 lines
7.1 KiB
JavaScript

#!/usr/bin/env node
/**
* OBS Virtual Camera with Noise Source
*
* This script connects to OBS via websocket and:
* 1. Creates a scene with a noise/random pattern source
* 2. Starts the virtual camera
*
* Prerequisites:
* - OBS Studio 28+ (has obs-websocket built-in)
* - Enable WebSocket Server in OBS: Tools -> WebSocket Server Settings
* - Set a password or disable authentication for local use
*
* Usage: node obs-noise-camera.mjs [start|stop] [--password=YOUR_PASSWORD]
*/
import OBSWebSocket from 'obs-websocket-js';
const obs = new OBSWebSocket();
const SCENE_NAME = 'NoiseCamera';
const SOURCE_NAME = 'RandomNoise';
async function connect(password) {
const url = 'ws://127.0.0.1:4455';
try {
const config = password ? { rpcVersion: 1, password } : { rpcVersion: 1 };
await obs.connect(url, password);
console.log('Connected to OBS WebSocket');
return true;
} catch (err) {
if (err.message?.includes('Authentication')) {
console.error('Authentication required. Use --password=YOUR_PASSWORD');
console.error('Or disable authentication in OBS: Tools -> WebSocket Server Settings');
} else {
console.error('Failed to connect to OBS:', err.message);
console.error('Make sure OBS is running and WebSocket Server is enabled');
}
return false;
}
}
async function createNoiseScene() {
// Check if scene exists
const { scenes } = await obs.call('GetSceneList');
const sceneExists = scenes.some(s => s.sceneName === SCENE_NAME);
if (!sceneExists) {
console.log(`Creating scene: ${SCENE_NAME}`);
await obs.call('CreateScene', { sceneName: SCENE_NAME });
}
// Switch to the scene
await obs.call('SetCurrentProgramScene', { sceneName: SCENE_NAME });
// Check if source exists in scene
const { sceneItems } = await obs.call('GetSceneItemList', { sceneName: SCENE_NAME });
const sourceExists = sceneItems.some(item => item.sourceName === SOURCE_NAME);
if (!sourceExists) {
console.log(`Creating noise source: ${SOURCE_NAME}`);
// Create a color source with shader/noise effect
// We'll use a color source and apply noise filter, or use browser source with noise
await obs.call('CreateInput', {
sceneName: SCENE_NAME,
inputName: SOURCE_NAME,
inputKind: 'color_source_v3',
inputSettings: {
color: 0xFF808080, // Gray base
width: 1920,
height: 1080
}
});
// Add a noise/static filter using the Shader filter if available
// Or we can use a different approach - let's try adding the "Noise Displacement" filter
try {
await obs.call('CreateSourceFilter', {
sourceName: SOURCE_NAME,
filterName: 'ShaderNoise',
filterKind: 'streamfx-filter-shader',
filterSettings: {
'Shader.Type': 0, // File
}
});
} catch (e) {
// StreamFX might not be installed, try built-in approach
console.log('Note: For best results, install StreamFX plugin for shader-based noise');
}
}
console.log(`Scene "${SCENE_NAME}" ready`);
}
async function createBrowserNoiseSource() {
// Alternative: Create a browser source with animated noise
const { scenes } = await obs.call('GetSceneList');
const sceneExists = scenes.some(s => s.sceneName === SCENE_NAME);
if (!sceneExists) {
console.log(`Creating scene: ${SCENE_NAME}`);
await obs.call('CreateScene', { sceneName: SCENE_NAME });
}
await obs.call('SetCurrentProgramScene', { sceneName: SCENE_NAME });
const { sceneItems } = await obs.call('GetSceneItemList', { sceneName: SCENE_NAME });
const sourceExists = sceneItems.some(item => item.sourceName === SOURCE_NAME);
if (!sourceExists) {
console.log(`Creating browser noise source: ${SOURCE_NAME}`);
// HTML/JS that generates animated noise
const noiseHtml = `data:text/html,
<!DOCTYPE html>
<html>
<head><style>body{margin:0;overflow:hidden}canvas{display:block}</style></head>
<body>
<canvas id="c"></canvas>
<script>
const c=document.getElementById('c'),x=c.getContext('2d');
c.width=1920;c.height=1080;
function draw(){
const d=x.createImageData(c.width,c.height);
for(let i=0;i<d.data.length;i+=4){
const v=Math.random()*255|0;
d.data[i]=d.data[i+1]=d.data[i+2]=v;
d.data[i+3]=255;
}
x.putImageData(d,0,0);
requestAnimationFrame(draw);
}
draw();
</script>
</body>
</html>`;
await obs.call('CreateInput', {
sceneName: SCENE_NAME,
inputName: SOURCE_NAME,
inputKind: 'browser_source',
inputSettings: {
url: noiseHtml,
width: 1920,
height: 1080,
fps: 30,
reroute_audio: false
}
});
}
console.log(`Browser noise source "${SOURCE_NAME}" ready`);
}
async function startVirtualCamera() {
const { outputActive } = await obs.call('GetVirtualCamStatus');
if (!outputActive) {
console.log('Starting virtual camera...');
await obs.call('StartVirtualCam');
console.log('Virtual camera started');
} else {
console.log('Virtual camera already running');
}
}
async function stopVirtualCamera() {
const { outputActive } = await obs.call('GetVirtualCamStatus');
if (outputActive) {
console.log('Stopping virtual camera...');
await obs.call('StopVirtualCam');
console.log('Virtual camera stopped');
} else {
console.log('Virtual camera not running');
}
}
async function getStatus() {
const { outputActive } = await obs.call('GetVirtualCamStatus');
const { currentProgramSceneName } = await obs.call('GetCurrentProgramScene');
console.log(`Virtual Camera: ${outputActive ? 'RUNNING' : 'STOPPED'}`);
console.log(`Current Scene: ${currentProgramSceneName}`);
}
async function main() {
const args = process.argv.slice(2);
const command = args.find(a => !a.startsWith('--')) || 'start';
const passwordArg = args.find(a => a.startsWith('--password='));
const password = passwordArg ? passwordArg.split('=')[1] : undefined;
if (!await connect(password)) {
process.exit(1);
}
try {
switch (command) {
case 'start':
await createBrowserNoiseSource();
await startVirtualCamera();
await getStatus();
break;
case 'stop':
await stopVirtualCamera();
break;
case 'status':
await getStatus();
break;
default:
console.log('Usage: node obs-noise-camera.mjs [start|stop|status] [--password=PASS]');
}
} catch (err) {
console.error('Error:', err.message);
} finally {
obs.disconnect();
}
}
main();