Skip to content

Commit

Permalink
feat: attempt to handle out of range features safely
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed Dec 7, 2019
1 parent 29e61bd commit 062e977
Show file tree
Hide file tree
Showing 39 changed files with 208 additions and 24 deletions.
11 changes: 9 additions & 2 deletions src/__tests__/tally.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { listVisibleInputs } from '../lib/tally'

import { readFileSync } from 'fs'
import { resolve } from 'path'
import { parseAtemState } from './util'
import { parseAtemState, createEmptyState } from './util'

function readJson (fileName: string) {
const filePath = resolve(__dirname, fileName)
Expand All @@ -12,7 +12,14 @@ function readJson (fileName: string) {
}

function loadRawState (file: string) {
return parseAtemState(readJson(`./tally/${file}-state.json`))
const loadedState = parseAtemState(readJson(`./tally/${file}-state.json`))

if (!loadedState.info.capabilities) {
const emptyState = createEmptyState()
loadedState.info.capabilities = emptyState.info.capabilities
}

return loadedState
}
function loadTally (file: string) {
const rawTally = readJson(`./tally/${file}-tally.json`) as Commands.TallyBySourceCommand['properties']
Expand Down
25 changes: 25 additions & 0 deletions src/__tests__/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,28 @@ export function parseAtemState (rawState: any): AtemState {

return state
}

export function createEmptyState () {
const state = new AtemState()

// These should be the maximum supported by any device.
// But they can also be whatever is needed to allow the tests to run without error
state.info.capabilities = {
mixEffects: 4,
sources: 40,
colorGenerators: 2,
auxilliaries: 6,
talkbackOutputs: 8,
mediaPlayers: 4,
serialPorts: 1,
maxHyperdecks: 4,
DVEs: 1,
stingers: 1,
superSources: 2,
talkbackOverSDI: 0,
multiViewers: 255,
downstreamKeyers: 4
}

return state
}
9 changes: 7 additions & 2 deletions src/commands/DeviceProfile/topologyCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class TopologyCommand extends DeserializedCommand<AtemCapabilites> {

public static deserialize (rawCommand: Buffer) {
const properties = {
MEs: rawCommand[0],
mixEffects: rawCommand[0],
sources: rawCommand[1],
colorGenerators: rawCommand[2],
auxilliaries: rawCommand[3],
Expand All @@ -18,7 +18,12 @@ export class TopologyCommand extends DeserializedCommand<AtemCapabilites> {
DVEs: rawCommand[8],
stingers: rawCommand[9],
superSources: rawCommand[10],
talkbackOverSDI: rawCommand[13]
talkbackOverSDI: rawCommand[13],

// TODO - define the below properly
multiViewers: 2,
downstreamKeyers: 2,
upstreamKeyers: 2
}

return new TopologyCommand(properties)
Expand Down
4 changes: 4 additions & 0 deletions src/commands/DownstreamKey/DownstreamKeyPropertiesCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export class DownstreamKeyPropertiesCommand extends DeserializedCommand<Downstre
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.downstreamKeyerId >= state.info.capabilities.downstreamKeyers) {
throw new Error(`DownstreamKeyer ${this.downstreamKeyerId} is not valid`)
}

state.video.getDownstreamKeyer(this.downstreamKeyerId).properties = this.properties
return `video.downstreamKeyers.${this.downstreamKeyerId}`
}
Expand Down
4 changes: 4 additions & 0 deletions src/commands/DownstreamKey/DownstreamKeySourcesCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export class DownstreamKeySourcesCommand extends DeserializedCommand<DownstreamK
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.downstreamKeyerId >= state.info.capabilities.downstreamKeyers) {
throw new Error(`DownstreamKeyer ${this.downstreamKeyerId} is not valid`)
}

state.video.getDownstreamKeyer(this.downstreamKeyerId).sources = this.properties
return `video.downstreamKeyers.${this.downstreamKeyerId}`
}
Expand Down
8 changes: 8 additions & 0 deletions src/commands/DownstreamKey/DownstreamKeyStateCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export class DownstreamKeyStateCommand extends DeserializedCommand<DownstreamKey
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.downstreamKeyerId >= state.info.capabilities.downstreamKeyers) {
throw new Error(`DownstreamKeyer ${this.downstreamKeyerId} is not valid`)
}

state.video.downstreamKeyers[this.downstreamKeyerId] = {
...state.video.getDownstreamKeyer(this.downstreamKeyerId),
...this.properties
Expand Down Expand Up @@ -61,6 +65,10 @@ export class DownstreamKeyStateV8Command extends DeserializedCommand<DownstreamK
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.downstreamKeyerId >= state.info.capabilities.downstreamKeyers) {
throw new Error(`DownstreamKeyer ${this.downstreamKeyerId} is not valid`)
}

state.video.downstreamKeyers[this.downstreamKeyerId] = {
...state.video.getDownstreamKeyer(this.downstreamKeyerId),
...this.properties
Expand Down
4 changes: 4 additions & 0 deletions src/commands/Media/MediaPlayerSourceCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export class MediaPlayerSourceUpdateCommand extends DeserializedCommand<MediaPla
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mediaPlayerId >= state.info.capabilities.mediaPlayers) {
throw new Error(`MediaPlayer ${this.mediaPlayerId} is not valid`)
}

state.media.players[this.mediaPlayerId] = {
...state.media.getMediaPlayer(this.mediaPlayerId),
...this.properties
Expand Down
4 changes: 4 additions & 0 deletions src/commands/Media/MediaPlayerStatusCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export class MediaPlayerStatusUpdateCommand extends DeserializedCommand<MediaPla
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mediaPlayerId >= state.info.capabilities.mediaPlayers) {
throw new Error(`MediaPlayer ${this.mediaPlayerId} is not valid`)
}

state.media.players[this.mediaPlayerId] = {
...state.media.getMediaPlayer(this.mediaPlayerId),
...this.properties
Expand Down
12 changes: 7 additions & 5 deletions src/commands/Media/MediaPoolClipDescription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { Util } from '../../lib/atemUtil'
export class MediaPoolClipDescriptionCommand extends DeserializedCommand<Omit<ClipBank, 'frames'>> {
public static readonly rawName = 'MPCS'

public readonly mediaPool: number
public readonly clipId: number

constructor (mediaPool: number, properties: Omit<ClipBank, 'frames'>) {
super(properties)

this.mediaPool = mediaPool
this.clipId = mediaPool
}

public static deserialize (rawCommand: Buffer) {
Expand All @@ -26,10 +26,12 @@ export class MediaPoolClipDescriptionCommand extends DeserializedCommand<Omit<Cl
}

public applyToState (state: AtemState) {
state.media.clipPool[this.mediaPool] = {
// TODO - validate ids

state.media.clipPool[this.clipId] = {
...this.properties,
frames: state.media.getClip(this.mediaPool).frames // TODO - lengthen/shorten array of frames?
frames: state.media.getClip(this.clipId).frames // TODO - lengthen/shorten array of frames?
}
return `media.clipPool.${this.mediaPool}`
return `media.clipPool.${this.clipId}`
}
}
11 changes: 7 additions & 4 deletions src/commands/Media/MediaPoolFrameDescription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,19 @@ export class MediaPoolFrameDescriptionCommand extends DeserializedCommand<StillF
return new MediaPoolFrameDescriptionCommand(mediaPool, frameIndex, properties)
}

public applyToState (state: AtemState) {
public applyToState (state: AtemState): string | string[] {
// TODO - validate ids

if (this.mediaPool === 0) {
// This is a still
state.media.stillPool[this.frameIndex] = this.properties
return `media.stillPool.${this.frameIndex}`
} else if (this.mediaPool < 3) {
const clipId = this.mediaPool - 1
// This is a clip
state.media.getClip(this.mediaPool - 1).frames[this.frameIndex] = this.properties
return `media.clipPool.${this.mediaPool - 1}.${this.frameIndex}`
state.media.getClip(clipId).frames[this.frameIndex] = this.properties
return `media.clipPool.${clipId}.frames.${this.frameIndex}`
}
return `media`
return []
}
}
4 changes: 4 additions & 0 deletions src/commands/MixEffects/FadeToBlack/FadeToBlackRateCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export class FadeToBlackRateUpdateCommand extends DeserializedCommand<{ rate: nu
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects) {
throw new Error(`MixEffect ${this.mixEffect} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.fadeToBlack = {
isFullyBlack: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export class FadeToBlackStateCommand extends DeserializedCommand<FadeToBlackProp
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects) {
throw new Error(`MixEffect ${this.mixEffect} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.fadeToBlack = {
rate: 0,
Expand Down
4 changes: 4 additions & 0 deletions src/commands/MixEffects/Key/MixEffectKeyChromaCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export class MixEffectKeyChromaUpdateCommand extends DeserializedCommand<Upstrea
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects || this.upstreamKeyerId >= state.info.capabilities.upstreamKeyers) {
throw new Error(`UpstreamKeyer ${this.mixEffect}-${this.upstreamKeyerId} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
const upstreamKeyer = mixEffect.getUpstreamKeyer(this.upstreamKeyerId)
upstreamKeyer.chromaSettings = {
Expand Down
4 changes: 4 additions & 0 deletions src/commands/MixEffects/Key/MixEffectKeyDVECommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ export class MixEffectKeyDVEUpdateCommand extends DeserializedCommand<UpstreamKe
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects || this.upstreamKeyerId >= state.info.capabilities.upstreamKeyers) {
throw new Error(`UpstreamKeyer ${this.mixEffect}-${this.upstreamKeyerId} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
const upstreamKeyer = mixEffect.getUpstreamKeyer(this.upstreamKeyerId)
upstreamKeyer.dveSettings = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ export class MixEffectKeyFlyKeyframeGetCommand extends DeserializedCommand<Upstr
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects || this.upstreamKeyerId >= state.info.capabilities.upstreamKeyers) {
throw new Error(`UpstreamKeyer ${this.mixEffect}-${this.upstreamKeyerId} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
const upstreamKeyer = mixEffect.getUpstreamKeyer(this.upstreamKeyerId)
upstreamKeyer.flyKeyframes[this.properties.keyFrameId] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export class MixEffectKeyFlyPropertiesGetCommand extends DeserializedCommand<Ups
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects || this.upstreamKeyerId >= state.info.capabilities.upstreamKeyers) {
throw new Error(`UpstreamKeyer ${this.mixEffect}-${this.upstreamKeyerId} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
const upstreamKeyer = mixEffect.getUpstreamKeyer(this.upstreamKeyerId)
upstreamKeyer.flyProperties = {
Expand Down
4 changes: 4 additions & 0 deletions src/commands/MixEffects/Key/MixEffectKeyLumaCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ export class MixEffectKeyLumaUpdateCommand extends DeserializedCommand<UpstreamK
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects || this.upstreamKeyerId >= state.info.capabilities.upstreamKeyers) {
throw new Error(`UpstreamKeyer ${this.mixEffect}-${this.upstreamKeyerId} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
const upstreamKeyer = mixEffect.getUpstreamKeyer(this.upstreamKeyerId)
upstreamKeyer.lumaSettings = {
Expand Down
4 changes: 4 additions & 0 deletions src/commands/MixEffects/Key/MixEffectKeyOnAirCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export class MixEffectKeyOnAirUpdateCommand extends DeserializedCommand<{onAir:
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects || this.upstreamKeyerId >= state.info.capabilities.upstreamKeyers) {
throw new Error(`UpstreamKeyer ${this.mixEffect}-${this.upstreamKeyerId} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
const upstreamKeyer = mixEffect.getUpstreamKeyer(this.upstreamKeyerId)
upstreamKeyer.onAir = this.properties.onAir
Expand Down
4 changes: 4 additions & 0 deletions src/commands/MixEffects/Key/MixEffectKeyPatternCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ export class MixEffectKeyUpdateCommand extends DeserializedCommand<UpstreamKeyer
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects || this.upstreamKeyerId >= state.info.capabilities.upstreamKeyers) {
throw new Error(`UpstreamKeyer ${this.mixEffect}-${this.upstreamKeyerId} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
const upstreamKeyer = mixEffect.getUpstreamKeyer(this.upstreamKeyerId)
upstreamKeyer.patternSettings = {
Expand Down
13 changes: 10 additions & 3 deletions src/commands/MixEffects/Key/MixEffectKeyPropertiesGetCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ export class MixEffectKeyPropertiesGetCommand extends DeserializedCommand<Upstre
public static readonly rawName = 'KeBP'

public readonly mixEffect: number
public readonly upstreamKeyerId: number

constructor (mixEffect: number, properties: UpstreamKeyerBase) {
constructor (mixEffect: number, keyer: number, properties: UpstreamKeyerBase) {
super(properties)

this.mixEffect = mixEffect
this.upstreamKeyerId = keyer
}

public static deserialize (rawCommand: Buffer): MixEffectKeyPropertiesGetCommand {
const mixEffect = rawCommand[0]
const keyer = rawCommand[1]
const properties = {
upstreamKeyerId: rawCommand[1],
upstreamKeyerId: keyer,
mixEffectKeyType: rawCommand.readUInt8(2),
flyEnabled: rawCommand[5] === 1,
fillSource: rawCommand.readUInt16BE(6),
Expand All @@ -28,10 +31,14 @@ export class MixEffectKeyPropertiesGetCommand extends DeserializedCommand<Upstre
maskRight: rawCommand.readInt16BE(18)
}

return new MixEffectKeyPropertiesGetCommand(mixEffect, properties)
return new MixEffectKeyPropertiesGetCommand(mixEffect, keyer, properties)
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects || this.upstreamKeyerId >= state.info.capabilities.upstreamKeyers) {
throw new Error(`UpstreamKeyer ${this.mixEffect}-${this.upstreamKeyerId} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.upstreamKeyers[this.properties.upstreamKeyerId] = {
...mixEffect.getUpstreamKeyer(this.properties.upstreamKeyerId),
Expand Down
4 changes: 4 additions & 0 deletions src/commands/MixEffects/PreviewInputCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ export class PreviewInputUpdateCommand extends DeserializedCommand<InputSource>
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects) {
throw new Error(`MixEffect ${this.mixEffect} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.previewInput = this.properties.source
return `video.ME.${this.mixEffect}.previewInput`
Expand Down
4 changes: 4 additions & 0 deletions src/commands/MixEffects/ProgramInputCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export class ProgramInputUpdateCommand extends DeserializedCommand<InputSource>
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects) {
throw new Error(`MixEffect ${this.mixEffect} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.programInput = this.properties.source
return `video.ME.${this.mixEffect}.programInput`
Expand Down
4 changes: 4 additions & 0 deletions src/commands/MixEffects/Transition/TransitionDVECommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ export class TransitionDVEUpdateCommand extends DeserializedCommand<DVETransitio
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects) {
throw new Error(`MixEffect ${this.mixEffect} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.transitionSettings.DVE = {
...this.properties
Expand Down
4 changes: 4 additions & 0 deletions src/commands/MixEffects/Transition/TransitionDipCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export class TransitionDipUpdateCommand extends DeserializedCommand<DipTransitio
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects) {
throw new Error(`MixEffect ${this.mixEffect} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.transitionSettings.dip = {
...this.properties
Expand Down
4 changes: 4 additions & 0 deletions src/commands/MixEffects/Transition/TransitionMixCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export class TransitionMixUpdateCommand extends DeserializedCommand<MixTransitio
}

public applyToState (state: AtemState) {
if (!state.info.capabilities || this.mixEffect >= state.info.capabilities.mixEffects) {
throw new Error(`MixEffect ${this.mixEffect} is not valid`)
}

const mixEffect = state.video.getMe(this.mixEffect)
mixEffect.transitionSettings.mix = {
...this.properties
Expand Down
Loading

0 comments on commit 062e977

Please sign in to comment.