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
JavaScript
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
;