camstreamerlib
Version:
Helper library for CamStreamer ACAP applications.
352 lines (351 loc) • 14.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CamSwitcherAPI = void 0;
const zod_1 = require("zod");
const errors_1 = require("./errors/errors");
const utils_1 = require("./internal/utils");
const CamSwitcherAPI_1 = require("./types/CamSwitcherAPI");
const common_1 = require("./types/common");
const VapixAPI_1 = require("./VapixAPI");
const versionCompare_1 = require("./internal/versionCompare");
const constants_1 = require("./internal/constants");
const baseUrl = '/local/camswitcher/api';
class CamSwitcherAPI {
client;
CustomFormData;
vapixAgent;
constructor(client, CustomFormData = FormData) {
this.client = client;
this.CustomFormData = CustomFormData;
this.vapixAgent = new VapixAPI_1.VapixAPI(client, () => '');
}
static getProxyUrlPath = () => `${baseUrl}/proxy.cgi`;
static getWsEventsUrlPath = () => `/local/camswitcher/events`;
static getClipPreviewUrlPath = (id, storage) => `${baseUrl}/clip_preview.cgi?clip_name=${id}&storage=${storage}`;
async generateSilence(sampleRate, channels) {
await this.client.get(`${baseUrl}/generate_silence.cgi`, {
sample_rate: sampleRate.toString(),
channels,
});
}
async checkCameraTime() {
const data = await this.get(`${baseUrl}/camera_time.cgi`);
return zod_1.z.boolean().parse(data);
}
async getIpListFromNetworkCheck() {
const data = await this.get(`${baseUrl}/network_camera_list.cgi`);
return common_1.networkCameraListSchema.parse(data);
}
async getMaxFps(source) {
const data = await this.get(`${baseUrl}/get_max_framerate.cgi`, {
video_source: source.toString(),
});
return zod_1.z.number().parse(data);
}
async getStorageInfo() {
const data = await this.get(`${baseUrl}/get_storage.cgi`);
return CamSwitcherAPI_1.storageInfoListSchema.parse(data);
}
async wsAuthorization() {
const data = await this.get(`${baseUrl}/ws_authorization.cgi`);
return zod_1.z.string().parse(data);
}
async getOutputInfo() {
const data = await this.get(`${baseUrl}/output_info.cgi`);
return CamSwitcherAPI_1.outputInfoSchema.parse(data);
}
async getAudioPushInfo() {
const data = await this.get(`${baseUrl}/audio_push_info.cgi`);
return CamSwitcherAPI_1.audioPushInfoSchema.parse(data);
}
async getStreamSaveList() {
const data = await this.get(`${baseUrl}/streams.cgi`, { action: 'get' });
return CamSwitcherAPI_1.streamSaveLoadSchema.parse(data);
}
async getClipSaveList() {
const data = await this.get(`${baseUrl}/clips.cgi`, { action: 'get' });
return CamSwitcherAPI_1.clipSaveLoadSchema.parse(data);
}
async getPlaylistSaveList() {
const data = await this.get(`${baseUrl}/playlists.cgi`, { action: 'get' });
return CamSwitcherAPI_1.playlistSaveLoadSchema.parse(data);
}
async getTrackerSaveList() {
const data = await this.get(`${baseUrl}/trackers.cgi`, { action: 'get' });
return CamSwitcherAPI_1.trackerSaveLoadSchema.parse(data);
}
async setStreamSaveList(data) {
return await this.set(`${baseUrl}/streams.cgi`, data, { action: 'set' });
}
async setClipSaveList(data) {
return await this.set(`${baseUrl}/clips.cgi`, data, { action: 'set' });
}
async setPlaylistSaveList(data) {
return await this.set(`${baseUrl}/playlists.cgi`, data, { action: 'set' });
}
async setTrackerSaveList(data) {
return await this.set(`${baseUrl}/trackers.cgi`, data, { action: 'set' });
}
async playlistSwitch(playlistName) {
await this.get(`${baseUrl}/playlist_switch.cgi?playlist_name=${playlistName}`);
}
async playlistQueuePush(playlistName) {
await this.get(`${baseUrl}/playlist_queue_push.cgi?playlist_name=${playlistName}`);
}
async playlistQueueClear() {
await this.get(`${baseUrl}/playlist_queue_clear.cgi`);
}
async playlistQueueList() {
const data = await this.get(`${baseUrl}/playlist_queue_list.cgi`);
return CamSwitcherAPI_1.playlistQueueSchema.parse(data).playlistQueueList;
}
async playlistQueuePlayNext() {
await this.get(`${baseUrl}/playlist_queue_play_next.cgi`);
}
async addNewClip(file, clipType, storage, id, fileName) {
const formData = new this.CustomFormData();
formData.append('clip_name', id);
formData.append('clip_type', clipType);
formData.append('file', file, fileName);
const path = `${baseUrl}/clip_upload.cgi?storage=${storage}`;
const res = await this.client.post(path, formData);
const output = (await res.json());
if (output.status !== 200) {
throw new errors_1.AddNewClipError(output.message);
}
}
removeClip(id, storage) {
return this.get(`${baseUrl}/clip_remove.cgi`, { clip_name: id, storage });
}
async getClipList() {
const data = await this.get(`${baseUrl}/clip_list.cgi`);
return CamSwitcherAPI_1.clipListSchema.parse(data).clip_list;
}
setCamSwitchOptions(data, cameraFWVersion) {
const bitrateVapixParams = parseBitrateOptionsToBitrateVapixParams(cameraFWVersion, data.bitrateMode, data);
const saveData = {
video: {
resolution: data.resolution,
h264Profile: data.h264Profile,
fps: data.fps,
compression: data.compression,
govLength: data.govLength,
videoClipQuality: data.maximumBitRate,
bitrateVapixParams: bitrateVapixParams,
},
audio: {
sampleRate: data.audioSampleRate,
channelCount: data.audioChannelCount,
},
keyboard: data.keyboard,
};
return this.setParamFromCameraJSON(CSW_PARAM_NAMES.SETTINGS, saveData);
}
setGlobalAudioSettings(settings) {
let acceptedType = 'NONE';
if (settings.type === 'source' && settings.source) {
if ((0, utils_1.isClip)(settings.source)) {
acceptedType = 'CLIP';
}
else {
acceptedType = 'STREAM';
}
}
const data = {
type: acceptedType,
stream_name: settings.source,
clip_name: settings.source,
storage: settings.storage,
};
return this.setParamFromCameraJSON(CSW_PARAM_NAMES.MASTER_AUDIO, data);
}
setSecondaryAudioSettings(settings) {
const data = {
type: settings.type,
stream_name: settings.streamName ?? '',
clip_name: settings.clipName ?? '',
storage: settings.storage,
secondary_audio_level: settings.secondaryAudioLevel,
master_audio_level: settings.masterAudioLevel,
};
return this.setParamFromCameraJSON(CSW_PARAM_NAMES.SECONDARY_AUDIO, data);
}
setDefaultPlaylist(id) {
const value = JSON.stringify({ default_playlist_id: id });
return this.vapixAgent.setParameter({
[CSW_PARAM_NAMES.DEFAULT_PLAYLIST]: value,
}, null);
}
setPermanentRtspUrlToken(token) {
return this.vapixAgent.setParameter({ [CSW_PARAM_NAMES.RTSP_TOKEN]: token }, null);
}
async getCamSwitchOptions() {
const saveData = await this.getParamFromCameraAndJSONParse(CSW_PARAM_NAMES.SETTINGS);
if ((0, utils_1.isNullish)(saveData.video)) {
return saveData;
}
if (!(0, utils_1.isNullish)(saveData.video?.bitrateVapixParams)) {
const bitrateOptions = parseVapixParamsToBitrateOptions(saveData.video.bitrateVapixParams);
saveData.video.bitrateMode = bitrateOptions.bitrateMode;
saveData.video.maximumBitRate = bitrateOptions.maximumBitRate;
saveData.video.retentionTime = bitrateOptions.retentionTime;
saveData.video.bitRateLimit = bitrateOptions.bitRateLimit;
}
if (!(0, utils_1.isNullish)(saveData.video?.bitrateLimit)) {
saveData.video.maximumBitRate = saveData.video.bitrateLimit;
saveData.video.bitrateMode = 'MBR';
}
if (!(0, utils_1.isNullish)(saveData.video?.videoClipQuality)) {
saveData.video.maximumBitRate = saveData.video.videoClipQuality;
}
return {
...saveData.video,
audioSampleRate: saveData.audio.sampleRate,
audioChannelCount: saveData.audio.channelCount,
keyboard: saveData.keyboard,
};
}
async getGlobalAudioSettings() {
const settings = {
type: 'fromSource',
source: 'fromSource',
};
const res = await this.getParamFromCameraAndJSONParse(CSW_PARAM_NAMES.MASTER_AUDIO);
if (res.type === 'STREAM') {
settings.type = 'source';
settings.source = res.stream_name;
}
else if (res.type === 'CLIP') {
settings.type = 'source';
settings.source = res.clip_name;
settings.storage = res.storage;
}
return settings;
}
async getSecondaryAudioSettings() {
const res = await this.getParamFromCameraAndJSONParse(CSW_PARAM_NAMES.SECONDARY_AUDIO);
const settings = {
type: res.type ?? 'NONE',
streamName: res.stream_name,
clipName: res.clip_name,
storage: res.storage,
secondaryAudioLevel: res.secondary_audio_level ?? 1,
masterAudioLevel: res.master_audio_level ?? 1,
};
return settings;
}
async getPermanentRtspUrlToken() {
const paramName = CSW_PARAM_NAMES.RTSP_TOKEN;
const res = await this.vapixAgent.getParameter([paramName], null);
return res[paramName] ?? '';
}
async get(path, parameters = {}) {
const res = await this.client.get(path, parameters);
if (res.ok) {
const d = (await res.json());
return d.data;
}
else {
throw new Error(await (0, utils_1.responseStringify)(res));
}
}
async set(path, data, parameters = {}) {
const res = await this.client.post(path, JSON.stringify(data), parameters);
if (res.ok) {
const parsed = await res.json();
return parsed.message === 'OK';
}
else {
throw new Error(await (0, utils_1.responseStringify)(res));
}
}
setParamFromCameraJSON(paramName, data) {
const params = {};
params[paramName] = JSON.stringify(data);
return this.vapixAgent.setParameter(params, null);
}
async getParamFromCameraAndJSONParse(paramName) {
const data = await this.vapixAgent.getParameter([paramName], null);
if (data[paramName] !== undefined) {
try {
if (data[paramName] === '') {
return {};
}
else {
return JSON.parse(data[paramName] + '');
}
}
catch {
throw new Error('Error: in JSON parsing of ' + paramName + '. Cannot parse: ' + data[paramName]);
}
}
throw new Error("Error: no parametr '" + paramName + "' was found");
}
}
exports.CamSwitcherAPI = CamSwitcherAPI;
const CSW_PARAM_NAMES = {
SETTINGS: 'Camswitcher.Settings',
MASTER_AUDIO: 'Camswitcher.MasterAudio',
SECONDARY_AUDIO: 'Camswitcher.SecondaryAudio',
RTSP_TOKEN: 'Camswitcher.RTSPAccessToken',
DEFAULT_PLAYLIST: 'Camswitcher.DefaultPlaylist',
};
const parseBitrateOptionsToBitrateVapixParams = (firmWareVersion, bitrateMode, cameraOptions) => {
if (!(0, versionCompare_1.isFirmwareVersionAtLeast)(firmWareVersion, constants_1.FIRMWARE_WITH_BITRATE_MODES_SUPPORT)) {
return `videomaxbitrate=${cameraOptions.maximumBitRate}`;
}
if (bitrateMode === null) {
return '';
}
const data = {
VBR: 'videobitratemode=vbr',
MBR: `videobitratemode=mbr&videomaxbitrate=${cameraOptions.maximumBitRate}`,
ABR: `videobitratemode=abr&videoabrtargetbitrate=${cameraOptions.maximumBitRate}&videoabrretentiontime=${cameraOptions.retentionTime}&videoabrmaxbitrate=${cameraOptions.bitRateLimit}`,
};
return data[bitrateMode];
};
const parseVapixParamsToBitrateOptions = (bitrateVapixParams) => {
const params = {};
const searchParams = new URLSearchParams(bitrateVapixParams);
searchParams.forEach((value, key) => {
params[key] = value;
});
const bitrateMode = params['videobitratemode'] !== undefined ? params['videobitratemode'].toUpperCase() : undefined;
const hasLowerFw = bitrateMode === undefined && params['videomaxbitrate'] !== undefined;
if (hasLowerFw) {
const maximumBitRate = parseInt(params['videomaxbitrate'] ?? '0', 10);
return {
bitrateMode: 'MBR',
maximumBitRate: maximumBitRate,
retentionTime: 1,
bitRateLimit: Math.floor(maximumBitRate * 1.1),
};
}
if (bitrateMode === 'ABR') {
const maximumBitRate = parseInt(params['videoabrtargetbitrate'] ?? '0', 10);
const retentionTime = parseInt(params['videoabrretentiontime'] ?? '0', 10);
const bitRateLimit = parseInt(params['videoabrmaxbitrate'] ?? '0', 10);
return {
bitrateMode,
maximumBitRate,
retentionTime,
bitRateLimit,
};
}
else if (bitrateMode === 'MBR') {
const maximumBitRate = params['videomaxbitrate'] !== undefined ? parseInt(params['videomaxbitrate'], 10) : null;
const oldMaximumBitrateParamValue = parseInt(params['videombrmaxbitrate'] ?? '0', 10);
return {
bitrateMode: bitrateMode,
maximumBitRate: maximumBitRate ?? oldMaximumBitrateParamValue,
retentionTime: 1,
bitRateLimit: Math.floor(maximumBitRate ?? oldMaximumBitrateParamValue * 1.1),
};
}
return {
bitrateMode: bitrateMode,
retentionTime: 1,
maximumBitRate: 0,
bitRateLimit: 0,
};
};