@binwus-org/onvif-nvt
Version:
Wrapper for ONVIF spec to control NVT (Network Video Transitter) devices.
655 lines (555 loc) • 19 kB
JavaScript
const Util = require('./utils/util');
const URL = require('url-parse');
const MODULE_MAP = {
access: './modules/access',
accessrules: './modules/accessrules',
action: './modules/action',
analytics: './modules/analytics',
core: './modules/core',
credential: './modules/credential',
deviceio: './modules/deviceio',
display: './modules/display',
door: './modules/door',
events: './modules/events',
imaging: './modules/imaging',
media: './modules/media',
media2: './modules/media2',
ptz: './modules/ptz',
receiver: './modules/receiver',
recording: './modules/recording',
replay: './modules/replay',
schedule: './modules/schedule',
search: './modules/search',
security: './modules/security',
snapshot: './utils/snapshot',
thermal: './modules/thermal',
videoanalytics: './modules/videoanalytics'
};
const MODULE_MAP_AFTER = {
core: function () {
this.core.init(this.serviceAddress, this.username, this.password);
},
media: function () {
this.media.init(this.timeDiff, this.serviceAddress, this.username, this.password);
},
snapshot: function () {
const defaultProfile = this.getDefaultProfile();
if (defaultProfile) {
const snapshotUri = defaultProfile.SnapshotUri.Uri;
this.snapshot.init(snapshotUri, this.username, this.password);
}
}
};
class Camera {
constructor() {
this.core = null;
this.access = null;
this.accessrules = null;
this.action = null;
this.analytics = null;
this.credential = null;
this.deviceio = null;
this.display = null;
this.door = null;
this.events = null;
this.imaging = null;
this.media = null;
this.media2 = null;
this.ptz = null;
this.receiver = null;
this.recording = null;
this.replay = null;
this.schedule = null;
this.search = null;
this.security = null;
this.snapshot = null;
this.thermal = null;
this.videoanalytics = null;
this.rootPath = null;
this.serviceAddress = null;
this.timeDiff = 0;
this.address = null;
this.port = null;
this.username = null;
this.password = null;
this.deviceInformation = null;
this.profileList = [];
this.defaultProfile = null;
}
add(name) {
if (!MODULE_MAP[name]) {
throw new Error(`Module '${name}' does not exist. Cannot add to Camera.`);
}
if (this[name]) {
return;
}
let Inst;
switch (name) {
case 'access':
Inst = require('./modules/access');
break;
case 'accessrules':
Inst = require('./modules/accessrules');
break;
case 'action':
Inst = require('./modules/action');
break;
case 'analytics':
Inst = require('./modules/analytics');
break;
case 'core':
Inst = require('./modules/core');
break;
case 'credential':
Inst = require('./modules/credential');
break;
case 'deviceio':
Inst = require('./modules/deviceio');
break;
case 'display':
Inst = require('./modules/display');
break;
case 'door':
Inst = require('./modules/door');
break;
case 'events':
Inst = require('./modules/events');
break;
case 'imaging':
Inst = require('./modules/imaging');
break;
case 'media':
Inst = require('./modules/media');
break;
case 'media2':
Inst = require('./modules/media2');
break;
case 'ptz':
Inst = require('./modules/ptz');
break;
case 'receiver':
Inst = require('./modules/receiver');
break;
case 'recording':
Inst = require('./modules/recording');
break;
case 'replay':
Inst = require('./modules/replay');
break;
case 'schedule':
Inst = require('./modules/schedule');
break;
case 'search':
Inst = require('./modules/search');
break;
case 'security':
Inst = require('./modules/security');
break;
case 'snapshot':
Inst = require('./utils/snapshot');
break;
case 'thermal':
Inst = require('./modules/thermal');
break;
case 'videoanalytics':
Inst = require('./modules/videoanalytics');
break;
}
const after = MODULE_MAP_AFTER[name] || (() => {});
this[name] = new Inst();
after.call(this);
}
connect(address, port, username, password, servicePath, callback) {
return new Promise((resolve, reject) => {
let errMsg = '';
if (errMsg = Util.isInvalidValue(address, 'string')) {
reject(new Error('The "address" argument for connect is invalid: ' + errMsg));
return;
}
port = port || 80;
username = username || null;
password = password || null;
servicePath = servicePath || '/onvif/device_service';
this.address = address;
this.port = port;
this.setAuth(username, password);
let serviceAddress = 'http://' + address;
if (port && port !== 80) {
serviceAddress = serviceAddress + ':' + port;
}
this.rootPath = serviceAddress;
serviceAddress = serviceAddress + servicePath;
this.serviceAddress = new URL(serviceAddress);
this.add('core');
return this.coreGetSystemDateAndTime().then(() => {
return this.coreGetServices();
}).then(() => {
return this.coreGetCapabilities();
}).then(() => {
return this.coreGetDeviceInformation();
}).then(() => {
return this.mediaGetProfiles();
}).then(() => {
return this.mediaGetStreamURI();
}).then(() => {
return this.mediaGetSnapshotUri();
}).then(() => {
return this.coreGetScopes();
}).then(() => {
const info = this.getInformation();
resolve(info);
}).catch(error => {
reject(error);
});
});
}
setAuth(username, password) {
if (typeof username === 'undefined') {
this.username = null;
} else {
this.username = username;
}
if (typeof password === 'undefined') {
this.password = null;
} else {
this.password = password;
}
}
getInformation() {
const o = this.deviceInformation;
if (o) {
return JSON.parse(JSON.stringify(o));
} else {
return null;
}
}
getDefaultProfile() {
return this.defaultProfile;
}
coreGetSystemDateAndTime() {
return new Promise((resolve, reject) => {
this.core.getSystemDateAndTime().then(results => {
this.timeDiff = this.core.getTimeDiff();
resolve();
}).catch(error => {
console.error(error);
reject(error);
});
});
}
coreGetServices() {
return new Promise((resolve, reject) => {
this.core.getServices(true).then(results => {
const response = results.data.GetServicesResponse;
const services = response.Service;
services.forEach(service => {
this.checkForProxy(service);
const namespace = service.Namespace;
if (namespace === 'http://www.onvif.org/ver10/device/wsdl') {
this.core.version = service.Version;
} else if (namespace === 'http://www.onvif.org/ver10/media/wsdl') {
this.add('media');
if (this.media) {
this.media.init(this.timeDiff, new URL(service.XAddr), this.username, this.password);
this.media.version = service.Version;
}
} else if (namespace === 'http://www.onvif.org/ver10/events/wsdl') {
this.add('events');
if (this.events) {
this.events.init(this.timeDiff, new URL(service.XAddr), this.username, this.password);
this.events.version = service.Version;
}
} else if (namespace === 'http://www.onvif.org/ver20/ptz/wsdl') {
this.add('ptz');
if (this.ptz) {
this.ptz.init(this.timeDiff, new URL(service.XAddr), this.username, this.password);
this.ptz.version = service.Version;
}
} else if (namespace === 'http://www.onvif.org/ver20/imaging/wsdl') {
this.add('imaging');
if (this.imaging) {
this.imaging.init(this.timeDiff, new URL(service.XAddr), this.username, this.password);
this.imaging.version = service.Version;
}
} else if (namespace === 'http://www.onvif.org/ver10/deviceIO/wsdl') {
this.add('deviceio');
if (this.deviceio) {
this.deviceio.init(this.timeDiff, new URL(service.XAddr), this.username, this.password);
this.deviceio.version = service.Version;
}
} else if (namespace === 'http://www.onvif.org/ver20/analytics/wsdl') {
this.add('analytics');
if (this.analytics) {
this.analytics.init(this.timeDiff, new URL(service.XAddr), this.username, this.password);
this.analytics.version = service.Version;
}
}
});
resolve();
}).catch(error => {
console.error(error);
resolve();
});
});
}
checkForProxy(service) {
const xaddrPath = new URL(service.XAddr);
if (xaddrPath.href === this.serviceAddress.href) {
return;
}
service.XAddr = this.rootPath + xaddrPath.pathname + xaddrPath.query;
}
coreGetCapabilities() {
return new Promise((resolve, reject) => {
this.core.getCapabilities().then(results => {
const c = results.data.GetCapabilitiesResponse.Capabilities;
if (!c) {
reject(new Error('Failed to initialize the device: No capabilities were found.'));
return;
}
if ('Analytics' in c) {
const analytics = c.Analytics;
this.checkForProxy(analytics);
if (analytics && 'XAddr' in analytics) {
if (!this.analytics) {
this.add('analytics');
if (this.analytics) {
const serviceAddress = new URL(analytics.XAddr);
this.analytics.init(this.timeDiff, serviceAddress, this.username, this.password);
}
}
if (this.analytics) {
if ('RuleSupport' in analytics && analytics.RuleSupport === 'true') {
this.analytics.ruleSupport = true;
}
if ('AnalyticsModuleSupport' in analytics && analytics.AnalyticsModuleSupport === 'true') {
this.analytics.analyticsModuleSupport = true;
}
}
}
}
if ('Events' in c) {
const events = c.Events;
this.checkForProxy(events);
if (events && 'XAddr' in events) {
if (!this.events) {
this.add('events');
if (this.events) {
const serviceAddress = new URL(events.XAddr);
this.events.init(this.timeDiff, serviceAddress, this.username, this.password);
}
}
if (this.events && this.analytics) {
if ('WSPullPointSupport' in events && events.WSPullPointSupport === 'true') {
this.analytics.wsPullPointSupport = true;
}
if ('WSSubscriptionPolicySupport' in events && events.WSSubscriptionPolicySupport === 'true') {
this.analytics.wsSubscriptionPolicySupport = true;
}
}
}
}
if ('Imaging' in c) {
const imaging = c.Imaging;
this.checkForProxy(imaging);
if (imaging && 'XAddr' in imaging) {
if (!this.imaging) {
this.add('imaging');
if (this.imaging) {
const serviceAddress = new URL(imaging.XAddr);
this.imaging.init(this.timeDiff, serviceAddress, this.username, this.password);
}
}
}
}
if ('Media' in c) {
const media = c.Media;
this.checkForProxy(media);
if (media && 'XAddr' in media) {
if (!this.media) {
this.add('media');
if (this.media) {
const serviceAddress = new URL(media.XAddr);
this.media.init(this.timeDiff, serviceAddress, this.username, this.password);
}
}
}
}
if ('PTZ' in c) {
const ptz = c.PTZ;
this.checkForProxy(ptz);
if (ptz && 'XAddr' in ptz) {
if (!this.ptz) {
this.add('ptz');
if (this.ptz) {
const serviceAddress = new URL(ptz.XAddr);
this.ptz.init(this.timeDiff, serviceAddress, this.username, this.password);
}
}
}
}
resolve();
}).catch(error => {
console.error(error);
reject(error);
});
});
}
coreGetDeviceInformation() {
return new Promise((resolve, reject) => {
this.core.getDeviceInformation().then(results => {
this.deviceInformation = results.data.GetDeviceInformationResponse;
resolve();
}).catch(error => {
console.error(error);
reject(error);
});
});
}
coreGetScopes() {
return new Promise((resolve, reject) => {
this.core.getScopes().then(results => {
const scopes = typeof results.data.GetScopesResponse.Scopes === 'undefined' || !Array.isArray(results.data.GetScopesResponse.Scopes) ? [] : results.data.GetScopesResponse.Scopes;
this.deviceInformation.Ptz = false;
scopes.forEach(scope => {
const s = scope.ScopeItem;
if (s.indexOf('onvif://www.onvif.org/hardware/') === 0) {
const hardware = s.split('/').pop();
this.deviceInformation.Hardware = hardware;
} else if (s.indexOf('onvif://www.onvif.org/type/Streaming') === 0) {
this.deviceInformation.Streaming = true;
} else if (s.indexOf('onvif://www.onvif.org/type/video_encoder') === 0) {
this.deviceInformation.VideoEncoder = true;
} else if (s.indexOf('onvif://www.onvif.org/type/audio_encoder') === 0) {
this.deviceInformation.AudiooEncoder = true;
} else if (s.indexOf('onvif://www.onvif.org/type/ptz') === 0) {
this.deviceInformation.Ptz = true;
} else if (s.indexOf('onvif://www.onvif.org/Profile/S') === 0) {
this.deviceInformation.ProfileS = true;
} else if (s.indexOf('onvif://www.onvif.org/Profile/C') === 0) {
this.deviceInformation.ProfileC = true;
} else if (s.indexOf('onvif://www.onvif.org/Profile/G') === 0) {
this.deviceInformation.ProfileG = true;
} else if (s.indexOf('onvif://www.onvif.org/Profile/Q') === 0) {
this.deviceInformation.ProfileQ = true;
} else if (s.indexOf('onvif://www.onvif.org/Profile/A') === 0) {
this.deviceInformation.ProfileA = true;
} else if (s.indexOf('onvif://www.onvif.org/Profile/T') === 0) {
this.deviceInformation.ProfileT = true;
} else if (s.indexOf('onvif://www.onvif.org/location/country/') === 0) {
const country = s.split('/').pop();
this.deviceInformation.Country = country;
} else if (s.indexOf('onvif://www.onvif.org/location/city/') === 0) {
const city = s.split('/').pop();
this.deviceInformation.City = city;
} else if (s.indexOf('onvif://www.onvif.org/name/') === 0) {
let name = s.split('/').pop();
name = name.replace(/_/g, ' ');
this.deviceInformation.Name = name;
}
});
resolve();
}).catch(error => {
console.error(error);
reject(error);
});
});
}
mediaGetProfiles() {
return new Promise((resolve, reject) => {
this.media.getProfiles().then(results => {
const profiles = results.data.GetProfilesResponse.Profiles;
if (!profiles) {
reject(new Error('Failed to initialize the device: The targeted device does not any media profiles.'));
return;
}
const profileList = this.parseProfiles(profiles);
this.profileList = this.profileList.concat(profileList);
resolve();
}).catch(error => {
console.error(error);
reject(error);
});
});
}
parseProfiles(profiles) {
const profileList = [];
if (!Array.isArray(profiles)) {
profiles = [profiles];
}
profiles.forEach(profile => {
profileList.push(profile);
if (!this.defaultProfile) {
this.defaultProfile = profile;
if (this.ptz) {
this.ptz.setDefaultProfileToken(profile.$.token);
}
}
});
return profileList;
}
getProfiles() {
return this.profileList;
}
mediaGetStreamURI() {
return new Promise((resolve, reject) => {
const protocols = ['UDP', 'HTTP', 'RTSP'];
let profileIndex = 0;
let protocolIndex = 0;
const getStreamUri = () => {
const profile = this.profileList[profileIndex];
if (profile) {
const protocol = protocols[protocolIndex];
if (protocol) {
const token = profile.$.token;
this.media.getStreamUri('RTP-Unicast', protocol, token).then(results => {
profile.StreamUri = results.data.GetStreamUriResponse.MediaUri;
++protocolIndex;
getStreamUri();
}).catch(error => {
console.error(error);
++protocolIndex;
getStreamUri();
});
} else {
++profileIndex;
protocolIndex = 0;
getStreamUri();
}
} else {
resolve();
}
};
getStreamUri();
});
}
mediaGetSnapshotUri() {
return new Promise((resolve, reject) => {
let profileIndex = 0;
const getSnapshotUri = () => {
const profile = this.profileList[profileIndex];
if (profile) {
this.media.getSnapshotUri(profile.$.token).then(results => {
try {
const service = {};
service.XAddr = results.data.GetSnapshotUriResponse.MediaUri.Uri;
this.checkForProxy(service);
profile.SnapshotUri = results.data.GetSnapshotUriResponse.MediaUri;
profile.SnapshotUri.Uri = service.XAddr;
} catch (e) {}
++profileIndex;
getSnapshotUri();
}).catch(error => {
console.error(error);
++profileIndex;
getSnapshotUri();
});
} else {
resolve();
}
};
getSnapshotUri();
});
}
}
module.exports = Camera;