homebridge-ring 9.25.0-beta.1 → 10.0.0-beta.0

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [10.0.0-beta.0](https://github.com/dgreif/ring/compare/v9.25.0-beta.1...v10.0.0-beta.0) (2022-02-18)
6
+
7
+
8
+ ### ⚠ BREAKING CHANGES
9
+
10
+ * `ring-client-api` only (homebridge not impacted) - All streaming will now be done using the new Ring api via WebRTC + WebSockets. For those using `camera.streamVideo`, you should not notice any difference. Anyone directly interacting with sip sessions, you will need to migrate to the new `LiveCall` api. It's fairly similar, and should be more reliable long term. A number of other sip-specific methods have been removed from the `RingCamera` api as well.
11
+ * Node 10 is no longer officially supported. It may still work, but could break without warning in the future.
12
+ * **homebridge:** The minimum supported homebridge version for this plugin is now 1.4.0
13
+ * **homebridge:** This change will break all existing automations related to cameras, and will likely move them back to the default room in HomeKit. Once updated, you will have to set up your automations again manually. This is necessary to get camera audio working again after the switch to Opus
14
+ * **homebridge:** Switching to Opus requires the camera to be removed and re-added to HomeKit. Until this is done, you will not receive audio from your cameras.
15
+
16
+ ### Features
17
+
18
+ * add floodlight pro ([f749d38](https://github.com/dgreif/ring/commit/f749d38e0528e631864bf20819692a466744e1f2)), closes [#889](https://github.com/dgreif/ring/issues/889)
19
+ * **homebridge:** stream opus audio without ffmpeg for transcoding ([ee142bc](https://github.com/dgreif/ring/commit/ee142bc0eee9dcfda41d162b2105535b8fdcbf9a))
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * drop support for node 10 ([509d556](https://github.com/dgreif/ring/commit/509d55670b3dd4efee1be968618f6bc6b142ae2b))
25
+ * **homebridge:** force cameras to be recreated in HomeKit ([94a1520](https://github.com/dgreif/ring/commit/94a15204f4a9ccbe94968be8db05add6baa5fcfa))
26
+ * **homebridge:** increase minimum homebridge version to 1.4.0 ([d5be74a](https://github.com/dgreif/ring/commit/d5be74aae45a3e353bcaba4fc42fa25c6b2fa37e))
27
+ * remove sip calling in favor of live calls ([7aeae37](https://github.com/dgreif/ring/commit/7aeae37e6e93b68747e14983cea9c0a9229faec6))
28
+ * use Buffer.from ([2b28d90](https://github.com/dgreif/ring/commit/2b28d9028fc7f2c2e11b11cf3b27cc03f097eab2))
29
+
5
30
  ## [9.25.0-beta.1](https://github.com/dgreif/ring/compare/v9.25.0-beta.0...v9.25.0-beta.1) (2022-02-14)
6
31
 
7
32
 
@@ -4,4 +4,3 @@ export * from './ring-camera';
4
4
  export * from './ring-chime';
5
5
  export * from './ring-device';
6
6
  export * from './ring-types';
7
- export * from './sip-session';
package/lib/api/index.js CHANGED
@@ -16,4 +16,3 @@ __exportStar(require("./ring-camera"), exports);
16
16
  __exportStar(require("./ring-chime"), exports);
17
17
  __exportStar(require("./ring-device"), exports);
18
18
  __exportStar(require("./ring-types"), exports);
19
- __exportStar(require("./sip-session"), exports);
@@ -16,7 +16,7 @@ export declare class LiveCall extends Subscribed {
16
16
  private readonly onWsOpen;
17
17
  private readonly onMessage;
18
18
  private readonly pc;
19
- readonly onCallAnswered: ReplaySubject<void>;
19
+ readonly onCallAnswered: ReplaySubject<string>;
20
20
  readonly onCallEnded: ReplaySubject<void>;
21
21
  private readonly audioSplitter;
22
22
  private readonly videoSplitter;
@@ -25,10 +25,6 @@ export declare class LiveCall extends Subscribed {
25
25
  constructor(sessionId: string, camera: RingCamera);
26
26
  private handleMessage;
27
27
  reservePort(bufferPorts?: number): Promise<number>;
28
- prepareTranscoder(transcodeVideoStream: boolean, ffmpegInputOptions: SpawnInput[] | undefined, audioPort: number, videoPort: number, sdpInput: string): {
29
- ffmpegInputArguments: SpawnInput[];
30
- inputSdpLines: string[];
31
- };
32
28
  startTranscoding(ffmpegOptions: FfmpegOptions): Promise<void>;
33
29
  activated: boolean;
34
30
  activate(): Promise<void>;
@@ -62,7 +62,7 @@ class LiveCall extends subscribed_1.Subscribed {
62
62
  case 'sdp':
63
63
  const answer = yield this.pc.createAnswer(message);
64
64
  this.sendMessage(Object.assign({ method: 'sdp' }, answer));
65
- this.onCallAnswered.next();
65
+ this.onCallAnswered.next(message.sdp);
66
66
  return;
67
67
  case 'ice':
68
68
  yield this.pc.addIceCandidate({
@@ -79,57 +79,22 @@ class LiveCall extends subscribed_1.Subscribed {
79
79
  return ports[0];
80
80
  });
81
81
  }
82
- prepareTranscoder(transcodeVideoStream, ffmpegInputOptions, audioPort, videoPort, sdpInput) {
83
- const ffmpegInputArguments = [
84
- '-hide_banner',
85
- '-protocol_whitelist',
86
- 'pipe,udp,rtp,file,crypto',
87
- '-acodec',
88
- 'libopus',
89
- '-f',
90
- 'sdp',
91
- ...(ffmpegInputOptions || []),
92
- '-i',
93
- sdpInput,
94
- ], inputSdpLines = [
95
- 'v=0',
96
- 'o=105202070 3747 461 IN IP4 127.0.0.1',
97
- 's=Talk',
98
- 'c=IN IP4 127.0.0.1',
99
- 'b=AS:380',
100
- 't=0 0',
101
- 'a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics',
102
- `m=audio ${audioPort} RTP/SAVP 101`,
103
- 'a=rtpmap:101 OPUS/48000/2',
104
- 'a=rtcp-fb:101 nack pli',
105
- 'a=fmtp:101 useinbandfec=1;sprop-stereo=0',
106
- 'a=rtcp-mux',
107
- ];
108
- if (transcodeVideoStream) {
109
- inputSdpLines.push(`m=video ${videoPort} RTP/SAVP 96`, 'a=rtpmap:96 H264/90000', 'a=rtcp-fb:96 nack', 'a=rtcp-fb:96 nack pli', 'a=fmtp:96 packetization-mode=1;profile-level-id=640029;level-asymmetry-allowed=1', 'a=rtcp-mux');
110
- this.addSubscriptions(this.onVideoRtp
111
- .pipe((0, operators_1.concatMap)((rtp) => {
112
- return this.videoSplitter.send(rtp.serialize(), {
113
- port: videoPort,
114
- });
115
- }))
116
- .subscribe());
117
- }
118
- this.addSubscriptions(this.onAudioRtp
119
- .pipe((0, operators_1.concatMap)((rtp) => {
120
- return this.audioSplitter.send(rtp.serialize(), {
121
- port: audioPort,
122
- });
123
- }))
124
- .subscribe());
125
- return {
126
- ffmpegInputArguments,
127
- inputSdpLines,
128
- };
129
- }
130
82
  startTranscoding(ffmpegOptions) {
131
83
  return __awaiter(this, void 0, void 0, function* () {
132
- const videoPort = yield this.reservePort(1), audioPort = yield this.reservePort(1), transcodeVideoStream = ffmpegOptions.video !== false, { ffmpegInputArguments, inputSdpLines } = this.prepareTranscoder(transcodeVideoStream, ffmpegOptions.input, audioPort, videoPort, 'pipe:'), ff = new camera_utils_1.FfmpegProcess({
84
+ const videoPort = yield this.reservePort(1), audioPort = yield this.reservePort(1), transcodeVideoStream = ffmpegOptions.video !== false, ffmpegInputArguments = [
85
+ '-hide_banner',
86
+ '-protocol_whitelist',
87
+ 'pipe,udp,rtp,file,crypto',
88
+ '-acodec',
89
+ 'libopus',
90
+ '-f',
91
+ 'sdp',
92
+ ...(ffmpegOptions.input || []),
93
+ '-i',
94
+ 'pipe:',
95
+ ], inputSdp = (yield (0, rxjs_1.firstValueFrom)(this.onCallAnswered))
96
+ .replace(/m=audio \d+/, `m=audio ${audioPort}`)
97
+ .replace(/m=video \d+/, `m=video ${videoPort}`), ff = new camera_utils_1.FfmpegProcess({
133
98
  ffmpegArgs: ffmpegInputArguments.concat(...(ffmpegOptions.audio || ['-acodec', 'aac']), ...(transcodeVideoStream
134
99
  ? ffmpegOptions.video || ['-vcodec', 'copy']
135
100
  : []), ...(ffmpegOptions.output || [])),
@@ -141,8 +106,24 @@ class LiveCall extends subscribed_1.Subscribed {
141
106
  info: util_1.logDebug,
142
107
  },
143
108
  });
109
+ this.addSubscriptions(this.onAudioRtp
110
+ .pipe((0, operators_1.concatMap)((rtp) => {
111
+ return this.audioSplitter.send(rtp.serialize(), {
112
+ port: audioPort,
113
+ });
114
+ }))
115
+ .subscribe());
116
+ if (transcodeVideoStream) {
117
+ this.addSubscriptions(this.onVideoRtp
118
+ .pipe((0, operators_1.concatMap)((rtp) => {
119
+ return this.videoSplitter.send(rtp.serialize(), {
120
+ port: videoPort,
121
+ });
122
+ }))
123
+ .subscribe());
124
+ }
144
125
  this.onCallEnded.subscribe(() => ff.stop());
145
- ff.writeStdin(inputSdpLines.filter((x) => Boolean(x)).join('\n'));
126
+ ff.writeStdin(inputSdp);
146
127
  // Activate the stream now that ffmpeg is ready to receive
147
128
  yield this.activate();
148
129
  });
@@ -2,12 +2,9 @@
2
2
  import { ActiveDing, CameraData, CameraDeviceSettingsData, CameraEventOptions, CameraEventResponse, CameraHealth, HistoryOptions, PeriodicFootageResponse, VideoSearchResponse } from './ring-types';
3
3
  import { RingRestClient } from './rest-client';
4
4
  import { BehaviorSubject, Subject } from 'rxjs';
5
- import { SrtpOptions } from '@homebridge/camera-utils';
6
5
  import { DeepPartial } from './util';
7
- import { FfmpegOptions, SipSession } from './sip-session';
8
- import { SipOptions } from './sip-call';
9
6
  import { Subscribed } from './subscribed';
10
- import { LiveCall } from './live-call';
7
+ import { FfmpegOptions, LiveCall } from './live-call';
11
8
  export declare function getBatteryLevel(data: Pick<CameraData, 'battery_life' | 'battery_life_2'>): number | null;
12
9
  export declare function getSearchQueryString(options: CameraEventOptions | (HistoryOptions & {
13
10
  accountId: string;
@@ -56,10 +53,6 @@ export declare class RingCamera extends Subscribed {
56
53
  setInHomeDoorbell(enable: boolean): Promise<boolean>;
57
54
  getHealth(): Promise<CameraHealth>;
58
55
  startLiveCall(): Promise<LiveCall>;
59
- startVideoOnDemand(): Promise<("" | ActiveDing) & import("./rest-client").ExtendedResponse>;
60
- private pollForActiveDing;
61
- private expiredDingIds;
62
- getSipConnectionDetails(): Promise<ActiveDing>;
63
56
  private removeDingById;
64
57
  processActiveDing(ding: ActiveDing): void;
65
58
  getEvents(options?: CameraEventOptions): Promise<CameraEventResponse & import("./rest-client").ExtendedResponse>;
@@ -92,15 +85,8 @@ export declare class RingCamera extends Subscribed {
92
85
  maxWaitMs?: number;
93
86
  force?: boolean;
94
87
  }): Promise<Buffer & import("./rest-client").ExtendedResponse>;
95
- getSipOptions(): Promise<SipOptions>;
96
- getUpdatedSipOptions(expiredDingId: string): Promise<SipOptions>;
97
- createSipSession(options?: {
98
- audio?: SrtpOptions;
99
- video?: SrtpOptions;
100
- skipFfmpegCheck?: boolean;
101
- }): Promise<SipSession>;
102
88
  recordToFile(outputPath: string, duration?: number): Promise<void>;
103
- streamVideo(ffmpegOptions: FfmpegOptions): Promise<SipSession>;
89
+ streamVideo(ffmpegOptions: FfmpegOptions): Promise<LiveCall>;
104
90
  subscribeToDingEvents(): Promise<void & import("./rest-client").ExtendedResponse>;
105
91
  unsubscribeFromDingEvents(): Promise<void & import("./rest-client").ExtendedResponse>;
106
92
  subscribeToMotionEvents(): Promise<void & import("./rest-client").ExtendedResponse>;
@@ -14,9 +14,7 @@ const ring_types_1 = require("./ring-types");
14
14
  const rest_client_1 = require("./rest-client");
15
15
  const rxjs_1 = require("rxjs");
16
16
  const operators_1 = require("rxjs/operators");
17
- const camera_utils_1 = require("@homebridge/camera-utils");
18
17
  const util_1 = require("./util");
19
- const sip_session_1 = require("./sip-session");
20
18
  const subscribed_1 = require("./subscribed");
21
19
  const live_call_1 = require("./live-call");
22
20
  const maxSnapshotRefreshSeconds = 15, fullDayMs = 24 * 60 * 60 * 1000;
@@ -82,7 +80,6 @@ class RingCamera extends subscribed_1.Subscribed {
82
80
  this.onMotionDetected = this.onActiveDings.pipe((0, operators_1.map)((dings) => dings.some((ding) => ding.motion || ding.kind === 'motion')), (0, operators_1.distinctUntilChanged)(), (0, operators_1.publishReplay)(1), (0, operators_1.refCount)());
83
81
  this.onMotionStarted = this.onMotionDetected.pipe((0, operators_1.filter)((currentlyDetected) => currentlyDetected), (0, operators_1.mapTo)(null), // no value needed, event is what matters
84
82
  (0, operators_1.share)());
85
- this.expiredDingIds = [];
86
83
  this.lastSnapshotTimestamp = 0;
87
84
  this.lastSnapshotTimestampLocal = 0;
88
85
  this.fetchingSnapshot = false;
@@ -251,42 +248,6 @@ class RingCamera extends subscribed_1.Subscribed {
251
248
  return new live_call_1.LiveCall(liveCall.data.session_id, this);
252
249
  });
253
250
  }
254
- startVideoOnDemand() {
255
- return this.restClient
256
- .request({
257
- method: 'POST',
258
- url: this.doorbotUrl('live_view'), // Ring app uses vod for battery cams, but doesn't appear to be necessary
259
- })
260
- .catch((e) => {
261
- var _a;
262
- if (((_a = e.response) === null || _a === void 0 ? void 0 : _a.statusCode) === 403) {
263
- const errorMessage = `Camera ${this.name} returned 403 when starting a live stream. This usually indicates that live streaming is blocked by Modes settings. Check your Ring app and verify that you are able to stream from this camera with the current Modes settings.`;
264
- (0, util_1.logError)(errorMessage);
265
- throw new Error(errorMessage);
266
- }
267
- throw e;
268
- });
269
- }
270
- pollForActiveDing() {
271
- // try every second until a new ding is received
272
- this.addSubscriptions((0, rxjs_1.interval)(1000)
273
- .pipe((0, operators_1.takeUntil)(this.onNewDing))
274
- .subscribe(() => {
275
- this.onRequestActiveDings.next(null);
276
- }));
277
- }
278
- getSipConnectionDetails() {
279
- return __awaiter(this, void 0, void 0, function* () {
280
- const vodPromise = (0, rxjs_1.firstValueFrom)(this.onNewDing), videoOnDemandDing = yield this.startVideoOnDemand();
281
- if (videoOnDemandDing && 'sip_from' in videoOnDemandDing) {
282
- // wired cams return a ding from live_view so we don't need to wait
283
- return videoOnDemandDing;
284
- }
285
- // battery cams return '' from live_view so we need to request active dings and wait
286
- this.pollForActiveDing();
287
- return vodPromise;
288
- });
289
- }
290
251
  removeDingById(idToRemove) {
291
252
  const allActiveDings = this.activeDings, otherDings = allActiveDings.filter((ding) => ding.id_str !== idToRemove);
292
253
  this.onActiveDings.next(otherDings);
@@ -297,7 +258,6 @@ class RingCamera extends subscribed_1.Subscribed {
297
258
  this.onNewDing.next(ding);
298
259
  setTimeout(() => {
299
260
  this.removeDingById(ding.id_str);
300
- this.expiredDingIds = this.expiredDingIds.filter((id) => id !== dingId);
301
261
  }, 65 * 1000); // dings last ~1 minute
302
262
  }
303
263
  getEvents(options = {}) {
@@ -418,49 +378,9 @@ class RingCamera extends subscribed_1.Subscribed {
418
378
  return response;
419
379
  });
420
380
  }
421
- getSipOptions() {
422
- return __awaiter(this, void 0, void 0, function* () {
423
- const activeDings = this.onActiveDings.getValue(), existingDing = activeDings
424
- .filter((ding) => !this.expiredDingIds.includes(ding.id_str))
425
- .slice()
426
- .reverse()[0], ding = existingDing || (yield this.getSipConnectionDetails());
427
- return {
428
- to: ding.sip_to,
429
- from: ding.sip_from,
430
- dingId: ding.id_str,
431
- localIp: yield (0, camera_utils_1.getDefaultIpAddress)(),
432
- };
433
- });
434
- }
435
- getUpdatedSipOptions(expiredDingId) {
436
- // Got a 480 from sip session, which means it's no longer active
437
- this.expiredDingIds.push(expiredDingId);
438
- return this.getSipOptions();
439
- }
440
- createSipSession(options = {}) {
441
- return __awaiter(this, void 0, void 0, function* () {
442
- const audioSplitter = new camera_utils_1.RtpSplitter(), audioRtcpSplitter = new camera_utils_1.RtpSplitter(), videoSplitter = new camera_utils_1.RtpSplitter(), videoRtcpSplitter = new camera_utils_1.RtpSplitter(), [sipOptions, ffmpegIsInstalled, audioPort, audioRtcpPort, videoPort, videoRtcpPort, [tlsPort],] = yield Promise.all([
443
- this.getSipOptions(),
444
- options.skipFfmpegCheck ? Promise.resolve(true) : (0, camera_utils_1.isFfmpegInstalled)(),
445
- audioSplitter.portPromise,
446
- audioRtcpSplitter.portPromise,
447
- videoSplitter.portPromise,
448
- videoRtcpSplitter.portPromise,
449
- (0, camera_utils_1.reservePorts)({ type: 'tcp' }),
450
- ]), rtpOptions = {
451
- audio: Object.assign({ port: audioPort, rtcpPort: audioRtcpPort }, (options.audio || (0, camera_utils_1.generateSrtpOptions)())),
452
- video: Object.assign({ port: videoPort, rtcpPort: videoRtcpPort }, (options.video || (0, camera_utils_1.generateSrtpOptions)())),
453
- };
454
- if (!ffmpegIsInstalled) {
455
- throw new Error('Ffmpeg is not installed. See https://github.com/dgreif/ring/wiki/FFmpeg for directions.');
456
- }
457
- return new sip_session_1.SipSession(sipOptions, rtpOptions, audioSplitter, audioRtcpSplitter, videoSplitter, videoRtcpSplitter, tlsPort, this);
458
- });
459
- }
460
381
  recordToFile(outputPath, duration = 30) {
461
382
  return __awaiter(this, void 0, void 0, function* () {
462
- const liveCall = yield this.startLiveCall();
463
- yield liveCall.startTranscoding({
383
+ const liveCall = yield this.streamVideo({
464
384
  output: ['-t', duration.toString(), outputPath],
465
385
  });
466
386
  yield (0, rxjs_1.firstValueFrom)(liveCall.onCallEnded);
@@ -468,9 +388,9 @@ class RingCamera extends subscribed_1.Subscribed {
468
388
  }
469
389
  streamVideo(ffmpegOptions) {
470
390
  return __awaiter(this, void 0, void 0, function* () {
471
- const sipSession = yield this.createSipSession();
472
- yield sipSession.start(ffmpegOptions);
473
- return sipSession;
391
+ const liveCall = yield this.startLiveCall();
392
+ yield liveCall.startTranscoding(ffmpegOptions);
393
+ return liveCall;
474
394
  });
475
395
  }
476
396
  subscribeToDingEvents() {
@@ -502,5 +422,3 @@ class RingCamera extends subscribed_1.Subscribed {
502
422
  }
503
423
  }
504
424
  exports.RingCamera = RingCamera;
505
- // SOMEDAY: extract image from video file?
506
- // ffmpeg -i input.mp4 -r 1 -f image2 image-%2d.png
@@ -82,6 +82,7 @@ export declare enum RingCameraKind {
82
82
  stickup_cam_v4 = "stickup_cam_v4",
83
83
  floodlight_v1 = "floodlight_v1",
84
84
  floodlight_v2 = "floodlight_v2",
85
+ floodlight_pro = "floodlight_pro",
85
86
  cocoa_camera = "cocoa_camera",
86
87
  cocoa_doorbell = "cocoa_doorbell",
87
88
  stickup_cam_mini = "stickup_cam_mini"
@@ -90,6 +90,7 @@ var RingCameraKind;
90
90
  RingCameraKind["stickup_cam_v4"] = "stickup_cam_v4";
91
91
  RingCameraKind["floodlight_v1"] = "floodlight_v1";
92
92
  RingCameraKind["floodlight_v2"] = "floodlight_v2";
93
+ RingCameraKind["floodlight_pro"] = "floodlight_pro";
93
94
  RingCameraKind["cocoa_camera"] = "cocoa_camera";
94
95
  RingCameraKind["cocoa_doorbell"] = "cocoa_doorbell";
95
96
  RingCameraKind["stickup_cam_mini"] = "stickup_cam_mini";
@@ -123,6 +124,7 @@ exports.RingCameraModel = {
123
124
  stickup_cam_v4: 'Spotlight Cam',
124
125
  floodlight_v1: 'Floodlight Cam',
125
126
  floodlight_v2: 'Floodlight Cam',
127
+ floodlight_pro: 'Floodlight Pro',
126
128
  cocoa_camera: 'Stick Up Cam',
127
129
  cocoa_doorbell: 'Doorbell Gen 2',
128
130
  stickup_cam_mini: 'Indoor Cam',
@@ -1,33 +1,4 @@
1
- /// <reference types="node" />
2
- import { RtpSplitter, SrtpOptions } from '@homebridge/camera-utils';
3
1
  import { DingKind } from './ring-types';
4
- export interface RtpStreamOptions extends SrtpOptions {
5
- port: number;
6
- rtcpPort: number;
7
- }
8
- export interface RtpOptions {
9
- audio: RtpStreamOptions;
10
- video: RtpStreamOptions;
11
- }
12
- export interface RtpStreamDescription extends RtpStreamOptions {
13
- ssrc?: number;
14
- iceUFrag?: string;
15
- icePwd?: string;
16
- }
17
- export interface RtpDescription {
18
- address: string;
19
- audio: RtpStreamDescription;
20
- video: RtpStreamDescription;
21
- }
22
- export declare function isStunMessage(message: Buffer): boolean;
23
- export declare function sendStunBindingRequest({ rtpDescription, rtpSplitter, rtcpSplitter, localUfrag, type, }: {
24
- rtpSplitter: RtpSplitter;
25
- rtcpSplitter: RtpSplitter;
26
- rtpDescription: RtpDescription;
27
- localUfrag?: string;
28
- type: 'video' | 'audio';
29
- }): void;
30
- export declare function createStunResponder(rtpSplitter: RtpSplitter): void;
31
2
  export interface LiveCallSession {
32
3
  alexa_port: 18443;
33
4
  app_session_token: string;
@@ -1,61 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseLiveCallSession = exports.createStunResponder = exports.sendStunBindingRequest = exports.isStunMessage = void 0;
4
- const util_1 = require("./util");
5
- const stun = require('stun');
6
- const stunMagicCookie = 0x2112a442; // https://tools.ietf.org/html/rfc5389#section-6
7
- function isStunMessage(message) {
8
- return message.length > 8 && message.readInt32BE(4) === stunMagicCookie;
9
- }
10
- exports.isStunMessage = isStunMessage;
11
- function sendStunBindingRequest({ rtpDescription, rtpSplitter, rtcpSplitter, localUfrag, type, }) {
12
- const message = stun.createMessage(1), remoteDescription = rtpDescription[type], { address } = rtpDescription, { iceUFrag, icePwd, port, rtcpPort } = remoteDescription;
13
- if (iceUFrag && icePwd && localUfrag) {
14
- // Full ICE supported. Send as formal stun request
15
- message.addUsername(iceUFrag + ':' + localUfrag);
16
- message.addMessageIntegrity(icePwd);
17
- stun
18
- .request(`${address}:${port}`, {
19
- socket: rtpSplitter.socket,
20
- message,
21
- })
22
- .then(() => (0, util_1.logDebug)(`${type} stun complete`))
23
- .catch((e) => {
24
- (0, util_1.logError)(`${type} stun error`);
25
- (0, util_1.logError)(e);
26
- });
27
- }
28
- else {
29
- // ICE not supported. Fire and forget the stun request for RTP and RTCP
30
- const encodedMessage = stun.encode(message);
31
- rtpSplitter.send(encodedMessage, { address, port }).catch(util_1.logError);
32
- rtcpSplitter
33
- .send(encodedMessage, { address, port: rtcpPort })
34
- .catch(util_1.logError);
35
- }
36
- }
37
- exports.sendStunBindingRequest = sendStunBindingRequest;
38
- function createStunResponder(rtpSplitter) {
39
- return rtpSplitter.addMessageHandler(({ message, info }) => {
40
- if (!isStunMessage(message)) {
41
- return null;
42
- }
43
- try {
44
- const decodedMessage = stun.decode(message), response = stun.createMessage(stun.constants.STUN_BINDING_RESPONSE, decodedMessage.transactionId);
45
- response.addXorAddress(info.address, info.port);
46
- rtpSplitter.send(stun.encode(response), info).catch(util_1.logError);
47
- }
48
- catch (e) {
49
- (0, util_1.logDebug)('Failed to Decode STUN Message');
50
- (0, util_1.logDebug)(message.toString('hex'));
51
- (0, util_1.logDebug)(e);
52
- }
53
- return null;
54
- });
55
- }
56
- exports.createStunResponder = createStunResponder;
3
+ exports.parseLiveCallSession = void 0;
57
4
  function parseLiveCallSession(sessionId) {
58
- const encodedSession = sessionId.split('.')[1], buff = new Buffer(encodedSession, 'base64'), text = buff.toString('ascii');
5
+ const encodedSession = sessionId.split('.')[1], buff = Buffer.from(encodedSession, 'base64'), text = buff.toString('ascii');
59
6
  return JSON.parse(text);
60
7
  }
61
8
  exports.parseLiveCallSession = parseLiveCallSession;