camstreamerlib 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ export declare type CamOverlayOptions = {
10
10
  auth?: string;
11
11
  serviceName?: string;
12
12
  serviceID?: number;
13
- camera?: number;
13
+ camera?: number | number[];
14
14
  };
15
15
  export declare type Field = {
16
16
  field_name: string;
@@ -44,11 +44,15 @@ export declare type Service = {
44
44
  schedule: string;
45
45
  name: string;
46
46
  identifier: string;
47
- camera: number;
47
+ cameraList: number[];
48
48
  };
49
49
  export declare type ServiceList = {
50
50
  services: Service[];
51
51
  };
52
+ export declare enum ImageType {
53
+ PNG = 0,
54
+ JPEG = 1
55
+ }
52
56
  export declare class CamOverlayAPI extends EventEmitter {
53
57
  private tls;
54
58
  private tlsInsecure;
@@ -57,7 +61,7 @@ export declare class CamOverlayAPI extends EventEmitter {
57
61
  private auth;
58
62
  private serviceName;
59
63
  private serviceID;
60
- private camera;
64
+ private cameraList;
61
65
  private callId;
62
66
  private sendMessages;
63
67
  private ws;
@@ -73,17 +77,20 @@ export declare class CamOverlayAPI extends EventEmitter {
73
77
  showCairoImage(cairoImage: string, posX: number, posY: number): Promise<CairoResponse>;
74
78
  showCairoImageAbsolute(cairoImage: string, posX: number, posY: number, width: number, height: number): Promise<CairoResponse>;
75
79
  removeImage(): Promise<CairoResponse>;
76
- sendMessage(msgJson: Message): Promise<CairoResponse | CairoCreateResponse | UploadImageResponse>;
77
- reportMsg(msg: string): void;
78
- reportErr(err: Error): void;
79
- reportClose(): void;
80
+ private sendMessage;
81
+ private sendBinaryMessage;
82
+ private reportMsg;
83
+ private reportErr;
84
+ private reportClose;
80
85
  updateCGText(fields: Field[]): Promise<void>;
81
86
  private formCoordinates;
82
87
  updateCGImage(path: string, coordinates?: string, x?: number, y?: number): Promise<void>;
88
+ updateCGImageFromData(imageType: ImageType, imageData: Buffer, coordinates?: string, x?: number, y?: number): Promise<void>;
83
89
  updateCGImagePos(coordinates?: string, x?: number, y?: number): Promise<void>;
84
- promiseCGUpdate(action: string, params: string): Promise<void>;
90
+ promiseCGUpdate(action: string, params: string, contentType?: string, data?: Buffer): Promise<void>;
85
91
  updateInfoticker(text: string): Promise<void>;
86
92
  setEnabled(enabled: boolean): Promise<void>;
87
93
  isEnabled(): Promise<boolean>;
88
94
  private getBaseVapixConnectionParams;
95
+ private compareCameraList;
89
96
  }
package/CamOverlayAPI.js CHANGED
@@ -9,14 +9,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.CamOverlayAPI = void 0;
12
+ exports.CamOverlayAPI = exports.ImageType = void 0;
13
13
  const WebSocket = require("ws");
14
14
  const EventEmitter = require("events");
15
15
  const Digest_1 = require("./Digest");
16
16
  const HTTPRequest_1 = require("./HTTPRequest");
17
+ var ImageType;
18
+ (function (ImageType) {
19
+ ImageType[ImageType["PNG"] = 0] = "PNG";
20
+ ImageType[ImageType["JPEG"] = 1] = "JPEG";
21
+ })(ImageType = exports.ImageType || (exports.ImageType = {}));
17
22
  class CamOverlayAPI extends EventEmitter {
18
23
  constructor(options) {
19
- var _a, _b, _c, _d, _e, _f, _g, _h;
24
+ var _a, _b, _c, _d, _e, _f, _g;
20
25
  super();
21
26
  this.ws = null;
22
27
  this.tls = (_a = options === null || options === void 0 ? void 0 : options.tls) !== null && _a !== void 0 ? _a : false;
@@ -25,11 +30,17 @@ class CamOverlayAPI extends EventEmitter {
25
30
  }
26
31
  this.tlsInsecure = (_b = options === null || options === void 0 ? void 0 : options.tlsInsecure) !== null && _b !== void 0 ? _b : false;
27
32
  this.ip = (_c = options === null || options === void 0 ? void 0 : options.ip) !== null && _c !== void 0 ? _c : '127.0.0.1';
28
- this.port = ((_d = options === null || options === void 0 ? void 0 : options.port) !== null && _d !== void 0 ? _d : this.tls) ? 443 : 80;
33
+ this.port = (_d = options === null || options === void 0 ? void 0 : options.port) !== null && _d !== void 0 ? _d : (this.tls ? 443 : 80);
29
34
  this.auth = (_e = options === null || options === void 0 ? void 0 : options.auth) !== null && _e !== void 0 ? _e : '';
30
35
  this.serviceName = (_f = options === null || options === void 0 ? void 0 : options.serviceName) !== null && _f !== void 0 ? _f : '';
31
36
  this.serviceID = (_g = options === null || options === void 0 ? void 0 : options.serviceID) !== null && _g !== void 0 ? _g : -1;
32
- this.camera = (_h = options === null || options === void 0 ? void 0 : options.camera) !== null && _h !== void 0 ? _h : 0;
37
+ this.cameraList = [0];
38
+ if (Array.isArray(options === null || options === void 0 ? void 0 : options.camera)) {
39
+ this.cameraList = options.camera;
40
+ }
41
+ else if (typeof (options === null || options === void 0 ? void 0 : options.camera) === 'number') {
42
+ this.cameraList = [options.camera];
43
+ }
33
44
  this.callId = 0;
34
45
  this.sendMessages = {};
35
46
  EventEmitter.call(this);
@@ -37,7 +48,7 @@ class CamOverlayAPI extends EventEmitter {
37
48
  connect() {
38
49
  return __awaiter(this, void 0, void 0, function* () {
39
50
  try {
40
- if (this.serviceID == -1) {
51
+ if (this.serviceID === -1) {
41
52
  this.serviceID = yield this.createService();
42
53
  }
43
54
  yield this.openWebsocket();
@@ -70,14 +81,14 @@ class CamOverlayAPI extends EventEmitter {
70
81
  if (s.id > maxID) {
71
82
  maxID = s.id;
72
83
  }
73
- if (s.identifier == this.serviceName && s.name == 'scripter') {
84
+ if (s.identifier === this.serviceName && s.name === 'scripter') {
74
85
  service = s;
75
86
  break;
76
87
  }
77
88
  }
78
- if (service != null) {
79
- if (service.camera == undefined || service.camera != this.camera) {
80
- service.camera = this.camera;
89
+ if (service !== null) {
90
+ if (service.cameraList === undefined || !this.compareCameraList(service.cameraList)) {
91
+ service.cameraList = this.cameraList;
81
92
  yield this.updateServices(servicesJson);
82
93
  return service.id;
83
94
  }
@@ -93,7 +104,7 @@ class CamOverlayAPI extends EventEmitter {
93
104
  schedule: '',
94
105
  name: 'scripter',
95
106
  identifier: this.serviceName,
96
- camera: this.camera,
107
+ cameraList: this.cameraList,
97
108
  };
98
109
  servicesJson.services.push(service);
99
110
  yield this.updateServices(servicesJson);
@@ -119,10 +130,11 @@ class CamOverlayAPI extends EventEmitter {
119
130
  rejectUnauthorized: !this.tlsInsecure,
120
131
  headers: {},
121
132
  };
122
- if (digestHeader != undefined) {
133
+ if (digestHeader !== undefined) {
123
134
  options.headers['Authorization'] = Digest_1.Digest.getAuthHeader(userPass[0], userPass[1], 'GET', '/local/camoverlay/ws', digestHeader);
124
135
  }
125
136
  this.ws = new WebSocket(addr, 'cairo-api', options);
137
+ this.ws.binaryType = 'arraybuffer';
126
138
  this.ws.on('open', () => {
127
139
  this.reportMsg('Websocket opened');
128
140
  resolve();
@@ -142,7 +154,7 @@ class CamOverlayAPI extends EventEmitter {
142
154
  }
143
155
  });
144
156
  this.ws.on('unexpected-response', (req, res) => __awaiter(this, void 0, void 0, function* () {
145
- if (res.statusCode == 401 && res.headers['www-authenticate'] != undefined)
157
+ if (res.statusCode === 401 && res.headers['www-authenticate'] !== undefined)
146
158
  this.openWebsocket(res.headers['www-authenticate']).then(resolve, reject);
147
159
  else {
148
160
  reject('Error: status code: ' + res.statusCode + ', ' + res.data);
@@ -165,16 +177,16 @@ class CamOverlayAPI extends EventEmitter {
165
177
  return this.sendMessage({ command: 'write_text', params: params });
166
178
  }
167
179
  uploadImageData(imgBuffer) {
168
- return this.sendMessage({
180
+ return this.sendBinaryMessage({
169
181
  command: 'upload_image_data',
170
- params: [imgBuffer.toString('base64')],
171
- });
182
+ params: [],
183
+ }, imgBuffer);
172
184
  }
173
185
  uploadFontData(fontBuffer) {
174
- return this.sendMessage({
186
+ return this.sendBinaryMessage({
175
187
  command: 'upload_font_data',
176
188
  params: [fontBuffer.toString('base64')],
177
- });
189
+ }, fontBuffer);
178
190
  }
179
191
  showCairoImage(cairoImage, posX, posY) {
180
192
  return this.sendMessage({
@@ -203,6 +215,24 @@ class CamOverlayAPI extends EventEmitter {
203
215
  }
204
216
  });
205
217
  }
218
+ sendBinaryMessage(msgJson, data) {
219
+ return new Promise((resolve, reject) => {
220
+ try {
221
+ this.sendMessages[this.callId] = { resolve, reject };
222
+ msgJson['call_id'] = this.callId++;
223
+ const jsonBuffer = Buffer.from(JSON.stringify(msgJson));
224
+ const header = new ArrayBuffer(5);
225
+ const headerView = new DataView(header);
226
+ headerView.setInt8(0, 1);
227
+ headerView.setInt32(1, jsonBuffer.byteLength);
228
+ const msgBuffer = Buffer.concat([Buffer.from(header), jsonBuffer, data]);
229
+ this.ws.send(msgBuffer);
230
+ }
231
+ catch (err) {
232
+ this.reportErr(new Error(`Send binary message error: ${err}`));
233
+ }
234
+ });
235
+ }
206
236
  reportMsg(msg) {
207
237
  this.emit('msg', msg);
208
238
  }
@@ -213,6 +243,10 @@ class CamOverlayAPI extends EventEmitter {
213
243
  this.emit('close');
214
244
  }
215
245
  reportClose() {
246
+ for (const callId in this.sendMessages) {
247
+ this.sendMessages[callId].reject(new Error('Connection lost'));
248
+ }
249
+ this.sendMessages = {};
216
250
  this.emit('close');
217
251
  }
218
252
  updateCGText(fields) {
@@ -220,30 +254,38 @@ class CamOverlayAPI extends EventEmitter {
220
254
  for (let field of fields) {
221
255
  const name = field.field_name;
222
256
  field_specs += `&${name}=${field.text}`;
223
- if (field.color != undefined) {
257
+ if (field.color !== undefined) {
224
258
  field_specs += `&${name}_color=${field.color}`;
225
259
  }
226
260
  }
227
261
  return this.promiseCGUpdate('update_text', field_specs);
228
262
  }
229
263
  formCoordinates(coordinates, x, y) {
230
- return coordinates != '' ? `&coord_system=${coordinates}&pos_x=${x}&pos_y=${y}` : '';
264
+ return coordinates !== '' ? `&coord_system=${coordinates}&pos_x=${x}&pos_y=${y}` : '';
231
265
  }
232
266
  updateCGImage(path, coordinates = '', x = 0, y = 0) {
233
267
  const coord = this.formCoordinates(coordinates, x, y);
234
268
  const update = `&image=${path}`;
235
269
  return this.promiseCGUpdate('update_image', update + coord);
236
270
  }
271
+ updateCGImageFromData(imageType, imageData, coordinates = '', x = 0, y = 0) {
272
+ const coord = this.formCoordinates(coordinates, x, y);
273
+ const contentType = imageType === ImageType.PNG ? 'image/png' : 'image/jpeg';
274
+ return this.promiseCGUpdate('update_image', coord, contentType, imageData);
275
+ }
237
276
  updateCGImagePos(coordinates = '', x = 0, y = 0) {
238
277
  const coord = this.formCoordinates(coordinates, x, y);
239
278
  return this.promiseCGUpdate('update_image', coord);
240
279
  }
241
- promiseCGUpdate(action, params) {
280
+ promiseCGUpdate(action, params, contentType, data) {
242
281
  return __awaiter(this, void 0, void 0, function* () {
243
282
  const options = this.getBaseVapixConnectionParams();
244
283
  options.method = 'POST';
245
284
  options.path = encodeURI(`/local/camoverlay/api/customGraphics.cgi?action=${action}&service_id=${this.serviceID}${params}`);
246
- yield (0, HTTPRequest_1.httpRequest)(options, '');
285
+ if (contentType && data) {
286
+ options.headers = { 'Content-Type': contentType };
287
+ }
288
+ yield (0, HTTPRequest_1.httpRequest)(options, data);
247
289
  });
248
290
  }
249
291
  updateInfoticker(text) {
@@ -251,7 +293,7 @@ class CamOverlayAPI extends EventEmitter {
251
293
  const options = this.getBaseVapixConnectionParams();
252
294
  options.method = 'GET';
253
295
  options.path = `/local/camoverlay/api/infoticker.cgi?service_id=${this.serviceID}&text=${text}`;
254
- yield (0, HTTPRequest_1.httpRequest)(options, '');
296
+ yield (0, HTTPRequest_1.httpRequest)(options);
255
297
  });
256
298
  }
257
299
  setEnabled(enabled) {
@@ -259,7 +301,7 @@ class CamOverlayAPI extends EventEmitter {
259
301
  const options = this.getBaseVapixConnectionParams();
260
302
  options.method = 'POST';
261
303
  options.path = encodeURI(`/local/camoverlay/api/enabled.cgi?id_${this.serviceID}=${enabled ? 1 : 0}`);
262
- yield (0, HTTPRequest_1.httpRequest)(options, '');
304
+ yield (0, HTTPRequest_1.httpRequest)(options);
263
305
  });
264
306
  }
265
307
  isEnabled() {
@@ -267,11 +309,11 @@ class CamOverlayAPI extends EventEmitter {
267
309
  const options = this.getBaseVapixConnectionParams();
268
310
  options.method = 'GET';
269
311
  options.path = '/local/camoverlay/api/services.cgi?action=get';
270
- const response = (yield (0, HTTPRequest_1.httpRequest)(options, ''));
312
+ const response = (yield (0, HTTPRequest_1.httpRequest)(options));
271
313
  const data = JSON.parse(response);
272
314
  for (let service of data.services) {
273
- if (service.id == this.serviceID) {
274
- return service.enabled == 1;
315
+ if (service.id === this.serviceID) {
316
+ return service.enabled === 1;
275
317
  }
276
318
  }
277
319
  throw new Error('Service not found.');
@@ -286,5 +328,9 @@ class CamOverlayAPI extends EventEmitter {
286
328
  rejectUnauthorized: !this.tlsInsecure,
287
329
  };
288
330
  }
331
+ compareCameraList(cameraList) {
332
+ return (this.cameraList.length === cameraList.length &&
333
+ this.cameraList.every((element, index) => element === cameraList[index]));
334
+ }
289
335
  }
290
336
  exports.CamOverlayAPI = CamOverlayAPI;
package/HTTPRequest.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import * as http from 'http';
3
4
  export declare type HttpRequestOptions = {
4
5
  method?: string;
@@ -13,4 +14,4 @@ export declare type HttpRequestOptions = {
13
14
  };
14
15
  rejectUnauthorized?: boolean;
15
16
  };
16
- export declare function httpRequest(options: HttpRequestOptions, postData?: string, noWaitForData?: boolean): Promise<string | http.IncomingMessage>;
17
+ export declare function httpRequest(options: HttpRequestOptions, postData?: Buffer | string, noWaitForData?: boolean): Promise<string | http.IncomingMessage>;
package/README.md CHANGED
@@ -197,7 +197,7 @@ Module for easy control of CamOverlay drawing API. For more details on supported
197
197
 
198
198
  ### methods
199
199
  #### CameraVapix(options)
200
- Options parameter contains access to the camera, service name, service ID and camera. If service ID is not specified, service is automatically created/selected based on serviceName. Specify video channel using parameter camera (in which View Area overlay will be shown). If omitted the default value camera=0 is used.
200
+ Options parameter contains access to the camera, service name, service ID and camera. If service ID is not specified, service is automatically created/selected based on serviceName. Specify video channel using parameter camera (in which View Area overlay will be shown). If you need to specify multiple video channels, you can use an array: `camera=[0,1]`. If omitted the default value `camera=0` is used.
201
201
  ```javascript
202
202
  options = {
203
203
  'ip': '127.0.0.1',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camstreamerlib",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "Helper library for CamStreamer ACAP applications.",
5
5
  "prettier": "@camstreamer/prettier-config",
6
6
  "dependencies": {