Skip to content

Commit

Permalink
Merge branch 'contribute/EAV-269' into release51
Browse files Browse the repository at this point in the history
  • Loading branch information
mint-dewit committed Jul 9, 2024
2 parents f6a3609 + dc719c8 commit be3ee42
Show file tree
Hide file tree
Showing 15 changed files with 569 additions and 69 deletions.
63 changes: 61 additions & 2 deletions packages/timeline-state-resolver-types/src/integrations/vmix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ export enum VMixCommand {
STOP_EXTERNAL = 'STOP_EXTERNAL',
OVERLAY_INPUT_IN = 'OVERLAY_INPUT_IN',
OVERLAY_INPUT_OUT = 'OVERLAY_INPUT_OUT',
SET_INPUT_OVERLAY = 'SET_INPUT_OVERLAY',
SET_LAYER_INPUT = 'SET_LAYER_INPUT',
SET_LAYER_ZOOM = 'SET_LAYER_ZOOM',
SET_LAYER_PAN_X = 'SET_LAYER_PAN_X',
SET_LAYER_PAN_Y = 'SET_LAYER_PAN_Y',
SET_LAYER_CROP = 'SET_LAYER_CROP',
SCRIPT_START = 'SCRIPT_START',
SCRIPT_STOP = 'SCRIPT_STOP',
SCRIPT_STOP_ALL = 'SCRIPT_STOP_ALL',
Expand Down Expand Up @@ -166,9 +170,19 @@ export interface TimelineContentVMixInput extends TimelineContentVMixBase {

transform?: VMixTransform

/** List of input (Multi View) overlays; indexes start from 1 */
/**
* List of input (Multi View) overlays; indexes start from 1
* @deprecated Use `layers` instead. If both `layers` and `overlays` are provided, `overlays` will be discarded
*/
overlays?: VMixInputOverlays

/**
* List of input Layers.
* Indexes start from 1.
* Requires vMix 27+.
*/
layers?: VMixLayers

/** An array of file paths to load into a List input. Uses Windows-style path separators (\\). Only applies to List inputs. */
listFilePaths?: string[]

Expand Down Expand Up @@ -221,10 +235,55 @@ export interface VMixTransform {
alpha: number
}

export interface VMixLayers {
[index: number]: VMixLayer
}

export interface VMixInputOverlays {
[index: number]: number | string
}

export interface VMixLayer {
input: string | number

/**
* Horizontal pan (-2 - 2)
* 0 = centered, -2 = 100% to left, 2 = 100% to right
*/
panX?: number
/**
* Vertical pan (-2 - 2)
* 0 = centered, -2 = 100% to bottom, 2 = 100% to top
*/
panY?: number

/**
* Scale (0 - 5)
*/
zoom?: number

/**
* Left crop (0 - 1)
* 0 = No Crop, 1 = Full Crop
*/
cropLeft?: number
/**
* Top crop (0 - 1)
* 0 = No Crop, 1 = Full Crop
*/
cropTop?: number
/**
* Right crop (0 - 1)
* 1 = No Crop, 0 = Full Crop
*/
cropRight?: number
/**
* Bottom crop (0 - 1)
* 1 = No Crop, 0 = Full Crop
*/
cropBottom?: number
}

export interface VMixTransition {
effect: VMixTransitionType

Expand Down
1 change: 1 addition & 0 deletions packages/timeline-state-resolver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
"@types/simple-oauth2": "^5.0.7",
"i18next-conv": "^13.1.1",
"i18next-parser": "^6.6.0",
"jest-mock-extended": "^3.0.7",
"json-schema-ref-parser": "^9.0.9",
"json-schema-to-typescript": "^10.1.5",
"vinyl-fs": "^3.0.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/* eslint-disable @typescript-eslint/unbound-method */
import { mock } from 'jest-mock-extended'
import { VMixCommandSender, VMixConnection } from '../connection'
import { VMixCommand } from 'timeline-state-resolver-types'

function createTestee() {
const mockConnection = mock<VMixConnection>()
const sender = new VMixCommandSender(mockConnection)
return { mockConnection, sender }
}

describe('VMixCommandSender', () => {
it('sends layer input', async () => {
const { sender, mockConnection } = createTestee()
await sender.sendCommand({
command: VMixCommand.SET_LAYER_INPUT,
index: 2,
input: 5,
value: 7,
})

expect(mockConnection.sendCommandFunction).toHaveBeenCalledTimes(1)
expect(mockConnection.sendCommandFunction).toHaveBeenLastCalledWith('SetMultiViewOverlay', {
input: 5,
value: '2,7',
})
})

it('sends layer crop', async () => {
const { sender, mockConnection } = createTestee()
await sender.sendCommand({
command: VMixCommand.SET_LAYER_CROP,
index: 2,
input: 5,
cropLeft: 0.1,
cropTop: 0.2,
cropRight: 0.3,
cropBottom: 0.4,
})

expect(mockConnection.sendCommandFunction).toHaveBeenCalledTimes(1)
expect(mockConnection.sendCommandFunction).toHaveBeenLastCalledWith('SetLayer2Crop', {
input: 5,
value: '0.1,0.2,0.3,0.4',
})
})

it('sends layer zoom', async () => {
const { sender, mockConnection } = createTestee()
await sender.sendCommand({
command: VMixCommand.SET_LAYER_ZOOM,
index: 3,
input: 6,
value: 1.5,
})

expect(mockConnection.sendCommandFunction).toHaveBeenCalledTimes(1)
expect(mockConnection.sendCommandFunction).toHaveBeenLastCalledWith('SetLayer3Zoom', {
input: 6,
value: 1.5,
})
})

it('sends layer panX', async () => {
const { sender, mockConnection } = createTestee()
await sender.sendCommand({
command: VMixCommand.SET_LAYER_PAN_X,
index: 3,
input: 6,
value: 1.5,
})

expect(mockConnection.sendCommandFunction).toHaveBeenCalledTimes(1)
expect(mockConnection.sendCommandFunction).toHaveBeenLastCalledWith('SetLayer3PanX', {
input: 6,
value: 1.5,
})
})

it('sends layer panY', async () => {
const { sender, mockConnection } = createTestee()
await sender.sendCommand({
command: VMixCommand.SET_LAYER_PAN_Y,
index: 3,
input: 6,
value: 1.5,
})

expect(mockConnection.sendCommandFunction).toHaveBeenCalledTimes(1)
expect(mockConnection.sendCommandFunction).toHaveBeenLastCalledWith('SetLayer3PanY', {
input: 6,
value: 1.5,
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function makeMockReportedState(): VMixState {
zoom: 1,
},
listFilePaths: undefined,
overlays: undefined,
layers: undefined,
playing: true,
},
'2': {
Expand All @@ -40,7 +40,7 @@ export function makeMockReportedState(): VMixState {
zoom: 1,
},
listFilePaths: undefined,
overlays: undefined,
layers: undefined,
playing: true,
},
},
Expand Down Expand Up @@ -75,7 +75,7 @@ export function makeMockReportedState(): VMixState {
zoom: 1,
},
listFilePaths: undefined,
overlays: undefined,
layers: undefined,
playing: true,
name: ADDED_INPUT_NAME_1,
},
Expand All @@ -93,7 +93,7 @@ export function makeMockReportedState(): VMixState {
zoom: 1,
},
listFilePaths: undefined,
overlays: undefined,
layers: undefined,
playing: true,
name: ADDED_INPUT_NAME_2,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,118 @@ describe('VMixStateDiffer', () => {

expect(busCommands.length).toBe(7) // all but Master
})

it('sets layer input when it starts to be controlled', () => {
const differ = createTestee()

const oldState = makeMockFullState()
const newState = makeMockFullState()

oldState.reportedState.existingInputs['99'] = differ.getDefaultInputState(99)

newState.reportedState.existingInputs['99'] = differ.getDefaultInputState(99)
newState.reportedState.existingInputs['99'].layers = {
2: {
input: 5,
},
}

const commands = differ.getCommandsToAchieveState(oldState, newState)

expect(commands.length).toBe(1)
expect(commands[0].command).toMatchObject({ command: VMixCommand.SET_LAYER_INPUT, value: 5, index: 2 })
})

it('sets layer zoom', () => {
const differ = createTestee()

const oldState = makeMockFullState()
const newState = makeMockFullState()

oldState.reportedState.existingInputs['99'] = differ.getDefaultInputState(99)
oldState.reportedState.existingInputs['99'].layers = {
2: {
input: 5,
},
}

newState.reportedState.existingInputs['99'] = differ.getDefaultInputState(99)
newState.reportedState.existingInputs['99'].layers = {
2: {
input: 5,
zoom: 1.5,
},
}

const commands = differ.getCommandsToAchieveState(oldState, newState)

expect(commands.length).toBe(1)
expect(commands[0].command).toMatchObject({ command: VMixCommand.SET_LAYER_ZOOM, value: 1.5, index: 2 })
})

it('sets layer pan', () => {
const differ = createTestee()

const oldState = makeMockFullState()
const newState = makeMockFullState()

oldState.reportedState.existingInputs['99'] = differ.getDefaultInputState(99)
oldState.reportedState.existingInputs['99'].layers = {
2: {
input: 5,
},
}

newState.reportedState.existingInputs['99'] = differ.getDefaultInputState(99)
newState.reportedState.existingInputs['99'].layers = {
2: {
input: 5,
panX: -1,
panY: 2,
},
}

const commands = differ.getCommandsToAchieveState(oldState, newState)

expect(commands.length).toBe(2)
expect(commands[0].command).toMatchObject({ command: VMixCommand.SET_LAYER_PAN_X, value: -1, index: 2 })
expect(commands[1].command).toMatchObject({ command: VMixCommand.SET_LAYER_PAN_Y, value: 2, index: 2 })
})

it('sets layer crop', () => {
const differ = createTestee()

const oldState = makeMockFullState()
const newState = makeMockFullState()

oldState.reportedState.existingInputs['99'] = differ.getDefaultInputState(99)
oldState.reportedState.existingInputs['99'].layers = {
2: {
input: 5,
},
}

newState.reportedState.existingInputs['99'] = differ.getDefaultInputState(99)
newState.reportedState.existingInputs['99'].layers = {
2: {
input: 5,
cropLeft: 0.2,
cropRight: 0.7,
cropTop: 0.1,
cropBottom: 0.8,
},
}

const commands = differ.getCommandsToAchieveState(oldState, newState)

expect(commands.length).toBe(1)
expect(commands[0].command).toMatchObject({
command: VMixCommand.SET_LAYER_CROP,
index: 2,
cropLeft: 0.2,
cropRight: 0.7,
cropTop: 0.1,
cropBottom: 0.8,
})
})
})
Loading

0 comments on commit be3ee42

Please sign in to comment.