UNPKG

casparcg-state

Version:

Node.js Javascript/Typescript library for keeping and resolving a given state of CasparCG into commands for casparcg-connection.

349 lines 12.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.literal = exports.addCommands = exports.getLayer = exports.getChannel = exports.compareMixerValues = exports.setDefaultValue = exports.setMixerTransition = exports.setTransition = exports.isIAMCPCommand = exports.addContext = exports.compareAttrs = exports.fixPlayCommandInput = exports.getTimeSincePlay = exports.calculatePlayAttributes = exports.calculateSeek = exports.time2FramesChannel = exports.frames2TimeChannel = exports.time2Frames = exports.frames2Time = void 0; const api_1 = require("./api"); const _ = require("underscore"); const mixer_1 = require("./mixer"); // import { Command as CommandNS } from 'casparcg-connection' const casparcg_connection_1 = require("casparcg-connection"); function frames2Time(frames, fps) { fps = fps || 25; // Set default: // ms = frames * (1000 / fps) fps = fps < 1 ? 1 / fps : fps; return frames * (1000 / fps); } exports.frames2Time = frames2Time; function time2Frames(time, fps) { fps = fps || 25; // Set default: // frames = ms / (1000 / fps) fps = fps < 1 ? 1 / fps : fps; return Math.floor(time / (1000 / fps)); } exports.time2Frames = time2Frames; function frames2TimeChannel(frames, newChannel, oldChannel) { // ms = frames * (1000 / fps) const fps = newChannel.fps || (oldChannel ? oldChannel.fps : 0) || 25; return frames2Time(frames, fps); } exports.frames2TimeChannel = frames2TimeChannel; function time2FramesChannel(time, newChannel, oldChannel) { // frames = ms / (1000 / fps) const fps = newChannel.fps || (oldChannel ? oldChannel.fps : 0) || 25; return time2Frames(time, fps); } exports.time2FramesChannel = time2FramesChannel; /** * Calculate seek time needed to make the clip to play in sync * Returns seek, in frames */ function calculateSeek(newChannel, oldChannel, layer, timeSincePlay) { if (layer.looping && !layer.length) { // if we don't know the length of the loop, we can't seek.. return 0; } const seekStart = (layer.seek !== undefined ? layer.seek : layer.inPoint) || 0; let seekFrames = Math.max(0, time2FramesChannel(seekStart + (timeSincePlay || 0), newChannel, oldChannel)); const inPointFrames = layer.inPoint !== undefined ? time2FramesChannel(layer.inPoint, newChannel, oldChannel) : undefined; const lengthFrames = layer.length !== undefined ? time2FramesChannel(layer.length, newChannel, oldChannel) : undefined; if (layer.looping) { const seekSinceInPoint = seekFrames - (inPointFrames || 0); if (seekSinceInPoint > 0 && lengthFrames) { seekFrames = (inPointFrames || 0) + (seekSinceInPoint % lengthFrames); } } return seekFrames; } exports.calculateSeek = calculateSeek; function calculatePlayAttributes(timeSincePlay, nl, newChannel, oldChannel) { let inPointFrames; let lengthFrames; let seekFrames = 0; let channelLayout; let looping = false; if (nl.content === api_1.LayerContentType.MEDIA) { looping = !!nl.looping; inPointFrames = nl.inPoint !== undefined ? time2FramesChannel(nl.inPoint, newChannel, oldChannel) : undefined; lengthFrames = nl.length !== undefined ? time2FramesChannel(nl.length, newChannel, oldChannel) : undefined; seekFrames = calculateSeek(newChannel, oldChannel, nl, timeSincePlay); } if (nl.content === api_1.LayerContentType.MEDIA) { channelLayout = nl.channelLayout; } if (looping) { if (!seekFrames) seekFrames = 0; if (!inPointFrames) inPointFrames = 0; } else { if (!inPointFrames && !seekFrames) inPointFrames = undefined; } return { inPointFrames, lengthFrames, seekFrames, looping, channelLayout, }; } exports.calculatePlayAttributes = calculatePlayAttributes; function getTimeSincePlay(layer, currentTime, minTimeSincePlay) { let timeSincePlay = layer.playTime === undefined ? 0 : (layer.pauseTime || currentTime) - (layer.playTime || 0); if (timeSincePlay < minTimeSincePlay) { timeSincePlay = 0; } if (_.isNull(layer.playTime)) { // null indicates the start time is not relevant, like for a LOGICAL object, or an image timeSincePlay = null; } return timeSincePlay; } exports.getTimeSincePlay = getTimeSincePlay; function fixPlayCommandInput(o) { const o2 = {}; for (const key of Object.keys(o)) { const value = o[key]; if (value !== undefined) o2[key] = value; } return o2; } exports.fixPlayCommandInput = fixPlayCommandInput; function compareAttrs(obj0, obj1, attrs, minTimeSincePlay = 150, // [ms] strict) { let difference = null; let diff0 = ''; let diff1 = ''; const getValue = function (val) { if (val && val._transition) return val._value; if (val && val.getString) return val.getString(); return mixer_1.Mixer.getValue(val); }; const cmp = (a, b, name) => { if (name === 'playTime') { return Math.abs(a - b) > minTimeSincePlay; } else { return !_.isEqual(a, b); } }; if (obj0 && obj1) { if (strict) { _.each(attrs, (a) => { if (obj0[a].valueOf() !== obj1[a].valueOf()) { diff0 = obj0[a].valueOf() + ''; diff1 = obj1[a].valueOf() + ''; if (diff0 && diff0.length > 20) { diff0 = diff0.slice(0, 20) + '...'; } if (diff1 && diff1.length > 20) { diff1 = diff1.slice(0, 20) + '...'; } difference = a.toString() + ': ' + diff0 + '!==' + diff1; } }); } else { _.each(attrs, (a) => { if (cmp(getValue(obj0[a]), getValue(obj1[a]), a)) { diff0 = getValue(obj0[a]) + ''; diff1 = getValue(obj1[a]) + ''; if (diff0 && diff0.length > 20) { diff0 = diff0.slice(0, 20) + '...'; } if (diff1 && diff1.length > 20) { diff1 = diff1.slice(0, 20) + '...'; } difference = a.toString() + ': ' + diff0 + '!=' + diff1; } }); } } else { if ((obj0 && !obj1) || (!obj0 && obj1)) { difference = '' + !!obj0 + ' t/f ' + !!obj1; } } return difference; } exports.compareAttrs = compareAttrs; function addContext(cmd, context, layer) { const returnCmd = cmd; returnCmd.context = { context, layerId: layer ? layer.id : '', }; return returnCmd; } exports.addContext = addContext; /* eslint-disable */ function isIAMCPCommand(cmd) { return cmd && 'command' in cmd && cmd.command in casparcg_connection_1.Commands && cmd.params; } exports.isIAMCPCommand = isIAMCPCommand; function setTransition(options, channel, oldLayer, content, isRemove, isBg) { if (!options) options = {}; const comesFromBG = (transitionObj) => { if (oldLayer.nextUp && _.isObject(oldLayer.nextUp.media)) { const t0 = new api_1.Transition(transitionObj); const t1 = new api_1.Transition(oldLayer.nextUp.media.inTransition); return t0.getString() === t1.getString(); } return false; }; if (_.isObject(content)) { let transition; if (isRemove) { if (content.outTransition) { transition = new api_1.Transition(content.outTransition); } } else { if (oldLayer.playing && content.changeTransition) { transition = new api_1.Transition(content.changeTransition); } else if (content.inTransition && (isBg || !comesFromBG(content.inTransition))) { transition = new api_1.Transition(content.inTransition); } } if (transition) { _.extend(options, transition.getOptions(channel.fps)); } } return options; } exports.setTransition = setTransition; function setMixerTransition(options, channel, oldLayer, content, isRemove) { if (!options) options = {}; const comesFromBG = (transitionObj) => { if (oldLayer.nextUp && _.isObject(oldLayer.nextUp.media)) { const t0 = new api_1.Transition(transitionObj); const t1 = new api_1.Transition(oldLayer.nextUp.media.inTransition); return t0.getString() === t1.getString(); } return false; }; if (_.isObject(content)) { let transition; if (isRemove) { if (content.outTransition) { transition = new api_1.Transition(content.outTransition); } } else { if (oldLayer.playing && content.changeTransition) { transition = new api_1.Transition(content.changeTransition); } else if (content.inTransition && !comesFromBG(content.inTransition)) { transition = new api_1.Transition(content.inTransition); } } if (transition) { const transOpts = transition.getOptions(channel.fps); options.duration = transOpts.transition.duration; options.tween = transOpts.transition.tween; } } return options; } exports.setMixerTransition = setMixerTransition; function setDefaultValue(obj, key, value) { if (_.isArray(obj)) { _.each(obj, (o) => { setDefaultValue(o, key, value); }); } else { if (_.isArray(key)) { _.each(key, (k) => { setDefaultValue(obj, k, value); }); } else { if (!obj[key]) obj[key] = value; } } } exports.setDefaultValue = setDefaultValue; function compareMixerValues(layer, oldLayer, attr, attrs) { const val0 = mixer_1.Mixer.getValue((layer.mixer || {})[attr]); const val1 = mixer_1.Mixer.getValue((oldLayer.mixer || {})[attr]); if (attrs) { let diff = null; if (val0 && val1) { _.each(attrs, function (a) { if (val0[a] !== val1[a]) { diff = `${a}: ${val0[a]} != ${val1[a]}`; } }); return diff; } else { if ((val0 && !val1) || (!val0 && val1)) { return `${attr}: ${val0} != ${val1}`; } } } else if (_.isObject(val0) || _.isObject(val1)) { // @todo is this used anymore? if (!_.isObject(val0) && _.isObject(val1)) { return `${attr}: val0 is object, but val1 is not`; } else if (_.isObject(val0) && !_.isObject(val1)) { return `${attr}: val1 is object, but val0 is not`; } else { const omitAttrs = ['inTransition', 'changeTransition', 'outTransition']; const omit0 = _.omit(val0, omitAttrs); const omit1 = _.omit(val1, omitAttrs); if (!_.isEqual(omit0, omit1)) { return `${attr}: ${val0} != ${val1}`; } } } else { if (val0 !== val1) { return `${attr}: ${val0} !== ${val1}`; } } return null; } exports.compareMixerValues = compareMixerValues; function getChannel(state, channelNo) { return state.channels[channelNo] || { channelNo: channelNo, layers: {} }; } exports.getChannel = getChannel; function getLayer(state, channelNo, layerNo) { const channel = getChannel(state, channelNo); return (channel.layers[layerNo] || { content: api_1.LayerContentType.NOTHING, id: '', layerNo: layerNo, }); } exports.getLayer = getLayer; function addCommands(diff, ...commands) { for (const cmd of commands) { if (isIAMCPCommand(cmd)) { diff.cmds.push({ ...cmd, context: cmd.context, }); } else { diff.cmds.push(cmd); } } } exports.addCommands = addCommands; function literal(o) { return o; } exports.literal = literal; //# sourceMappingURL=util.js.map