TypeScript application that uses a Gamepad to control a Video mixer and multiple cameras.
To start using this project use the following steps:
- install node.js
- clone the repository (
git clone https://github.com/sensslen/Cgf.CameraControl.Main.git) - install dependencies by calling
npm install - edit Cli/src/config.json to match your setup or just create a new configuration and run the application with the config parameter.
- compile the app :
npm run build - run the application using
npm start(which runsnode Cli/dist/index.js) or pass a custom config vianode Cli/dist/index.js --config path/to/my/config.json
Configuration of the application is stored as a JSON file. There is a default configuration (Cli/src/config.json) which is loaded when the config parameter is omitted when starting the application. Additionally, the application may be started with a custom configuration that may be located anywhere on the file system.
The configuration file has three main sections: cams (camera connections), videoMixers (video mixer connections), and interfaces (controller/gamepad configurations).
{
"cams": [
{
"instance": 1,
"type": "websocket/ptzlanc",
"ip": "192.168.1.100",
"panTiltInvert": false,
"showTallyLight": true
},
{
"instance": 2,
"type": "signalr/ptzlanc",
"connectionUrl": "http://192.168.1.101:5000",
"connectionPort": "COM6",
"panTiltInvert": false
},
{
"instance": 3,
"type": "viscaoverip",
"ip": "192.168.1.102",
"port": 52381,
"panTiltInvert": false
}
],
"videoMixers": [
{
"instance": 1,
"type": "blackmagicdesign/atem",
"ip": "192.168.1.240",
"mixEffectBlock": 0
}
],
"interfaces": [
{
"instance": 1,
"type": "logitech/gamepadf310",
"serialNumber": "optional-serial-number",
"videoMixer": 1,
"enableChangingProgram": true,
"connectionChange": {
"type": "direct",
"default": {
"up": 1,
"right": 2,
"down": 3,
"left": 4
},
"alt": {
"up": 5,
"right": 6,
"down": 7,
"left": 8
},
"altLower": {
"up": 9,
"right": 10,
"down": 11,
"left": 12
}
},
"specialFunction": {
"default": {
"down": {
"type": "key",
"index": 1
},
"up": {
"type": "macroToggle",
"indexOn": 23,
"indexOff": 24,
"condition": {
"type": "key",
"key": 0
}
},
"left": {
"type": "macroLoop",
"indexes": [1, 2, 3]
}
}
},
"cameraMap": {
"1": 1,
"2": 2,
"3": 3,
"4": 4
}
}
]
}The cams array defines the camera connections that the application can control. Each camera has:
instance: Unique numeric identifier for the cameratype: Type of camera connection. Supported types:"websocket/ptzlanc": WebSocket-based PTZ LANC camera control"signalr/ptzlanc": SignalR-based PTZ LANC camera control"viscaoverip": VISCA over IP camera control
For detailed module documentation, see:
{
"instance": 1,
"type": "websocket/ptzlanc",
"ip": "192.168.1.100",
"panTiltInvert": false,
"showTallyLight": true
}ip: IP address of the camera controllerpanTiltInvert: (Optional, default: false) Invert pan/tilt controlsshowTallyLight: (Optional, default: true) Enable tally light on camera
{
"instance": 2,
"type": "signalr/ptzlanc",
"connectionUrl": "http://192.168.1.101:5000",
"connectionPort": "COM6",
"panTiltInvert": false
}connectionUrl: URL of the SignalR camera controllerconnectionPort: Serial port identifierpanTiltInvert: (Optional, default: false) Invert pan/tilt controls
{
"instance": 3,
"type": "viscaoverip",
"ip": "192.168.1.102",
"port": 52381,
"panTiltInvert": false
}ip: IP address of the cameraport: (Optional, default: 52381) UDP port of the camerapanTiltInvert: (Optional, default: false) Invert pan/tilt controls
The videoMixers array defines the video mixer connections. Multiple video mixers can be configured.
{
"instance": 1,
"type": "blackmagicdesign/atem",
"ip": "192.168.1.240",
"mixEffectBlock": 0
}instance: Unique numeric identifier for the video mixertype: Type of video mixer. Currently supported:"blackmagicdesign/atem"ip: IP address of the ATEM switchermixEffectBlock: Zero-based ME block index to control (0-3 depending on ATEM model)
The interfaces array defines the gamepad/controller configurations. Each gamepad connects to exactly one video mixer and controls the cameras mapped to it.
{
"instance": 1,
"type": "logitech/gamepadf310",
"serialNumber": "optional-serial-number",
"videoMixer": 1,
"enableChangingProgram": true,
"connectionChange": { /* ... */ },
"specialFunction": { /* ... */ },
"cameraMap": { /* ... */ }
}instance: Unique numeric identifier for the interfacetype: Type of gamepad. Supported types:"logitech/gamepadf310": Logitech F310 gamepad"logitech/rumblepad2": Logitech Rumblepad 2
serialNumber: (Optional) Serial number to identify specific gamepadvideoMixer: Instance number of the video mixer this interface controlsenableChangingProgram: (Optional, default: true) Allow this controller to cut/switch program output
The cameraMap maps ATEM input numbers to camera instance numbers:
"cameraMap": {
"1": 1, // ATEM input 1 -> camera instance 1
"2": 2, // ATEM input 2 -> camera instance 2
"3": 3, // ATEM input 3 -> camera instance 3
"4": 4 // ATEM input 4 -> camera instance 4
}Defines how the D-pad buttons change camera selection. Two types are supported:
Direct Mode: Maps each D-pad direction to a specific camera instance
"connectionChange": {
"type": "direct",
"default": {
"up": 1,
"right": 2,
"down": 3,
"left": 4
},
"alt": { // Optional: used when "alt" modifier is held
"up": 5,
"right": 6,
"down": 7,
"left": 8
},
"altLower": { // Optional: used when "altLower" modifier is held
"up": 9,
"right": 10,
"down": 11,
"left": 12
}
}Directional Mode: Allows navigation between cameras in a directional manner
"connectionChange": {
"type": "directional",
"directions": {
"1": { // From camera instance 1
"right": 2,
"down": 3
},
"2": { // From camera instance 2
"left": 1,
"down": 4
}
}
}Maps gamepad buttons (A, B, X, Y) to special functions. Available in default, alt, and altLower contexts.
Button directions: "up" (Y), "down" (A), "left" (X), "right" (B)
Key Toggle: Toggle an ATEM upstream key
{
"type": "key",
"index": 1 // Key index (1-based)
}Macro Loop: Cycle through a series of macros
{
"type": "macroLoop",
"indexes": [1, 2, 3] // Array of macro indexes to cycle through
}Macro Toggle: Toggle between two macros based on a condition
{
"type": "macroToggle",
"indexOn": 23, // Macro to run when condition is false
"indexOff": 24, // Macro to run when condition is true
"condition": {
"type": "key", // Check if a key is active
"key": 0 // Key index to check
}
}{
"type": "macroToggle",
"indexOn": 20,
"indexOff": 21,
"condition": {
"type": "aux_selection", // Check AUX output selection
"aux": 5, // AUX output index
"selection": 16 // Expected source selection
}
}Connection Change: Trigger camera selection change via button
{
"type": "connectionChange"
}A lightweight interface that reacts to the keyboard and supports only a subset of the gamepad functionality:
- Arrow keys pan/tilt the currently selected (preview) camera.
- Number keys 1–9 select the mixer input (and therefore the camera) of the same number.
- Enter and Space optionally perform a mixer
cutorautotransition (seetransitionKeysbelow).
It is added to the interfaces array just like a gamepad:
{
"instance": 2,
"type": "keyboard",
"videoMixer": 1,
"cameraMap": {
"1": 1,
"2": 2,
"3": 3
},
"moveSpeed": 1,
"stopDelayMs": 250,
"transitionKeys": {
"enter": "cut",
"space": "auto"
}
}instance: Unique numeric identifier for the interfacetype: Must be"keyboard"videoMixer: Instance number of the video mixer this interface controlscameraMap: Maps mixer input numbers (the number key pressed) to camera instance numbersmoveSpeed: (Optional, default:1) Pan/tilt speed in the range]0 .. 1]while an arrow key is heldstopDelayMs: (Optional, default:250) Time in milliseconds after the last arrow key event before movement is stopped. Because the terminal only reports key presses (there is no key-release event), movement is kept alive by the operating system's key repeat. This value must be larger than the key repeat interval so that holding an arrow key results in continuous movement.transitionKeys: (Optional) Binds the Enter and Space keys to a mixer transition. Each ofenter/spacemay be set to"cut"(immediate cut) or"auto"(transitioned cut), or omitted to leave that key unassigned.
Note: The keyboard interface reads from the terminal (standard input), so the application must be started in an interactive terminal (a TTY). Press
Ctrl+Cto quit.