ring-client-api 9.25.0-beta.1 → 10.0.0-beta.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.
- package/CHANGELOG.md +49 -0
- package/lib/api/index.d.ts +0 -1
- package/lib/api/index.js +0 -1
- package/lib/api/live-call.d.ts +7 -7
- package/lib/api/live-call.js +168 -75
- package/lib/api/peer-connection.d.ts +7 -4
- package/lib/api/peer-connection.js +25 -6
- package/lib/api/ring-camera.d.ts +3 -16
- package/lib/api/ring-camera.js +10 -102
- package/lib/api/ring-types.d.ts +8 -15
- package/lib/api/ring-types.js +2 -0
- package/package.json +5 -7
- package/lib/api/rtp-utils.d.ts +0 -55
- package/lib/api/rtp-utils.js +0 -61
- package/lib/api/sip-call.d.ts +0 -76
- package/lib/api/sip-call.js +0 -242
- package/lib/api/sip-session.d.ts +0 -41
- package/lib/api/sip-session.js +0 -216
- package/lib/api/sip.d.ts +0 -87
- package/lib/api/sip.js +0 -1314
package/lib/api/ring-camera.js
CHANGED
|
@@ -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;
|
|
@@ -148,6 +145,9 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
148
145
|
get isOffline() {
|
|
149
146
|
return this.data.alerts.connection === 'offline';
|
|
150
147
|
}
|
|
148
|
+
get isRingEdgeEnabled() {
|
|
149
|
+
return this.data.settings.sheila_settings.local_storage_enabled === true;
|
|
150
|
+
}
|
|
151
151
|
get hasInHomeDoorbell() {
|
|
152
152
|
const { chime_settings } = this.data.settings;
|
|
153
153
|
return (this.isDoorbot &&
|
|
@@ -234,57 +234,8 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
234
234
|
}
|
|
235
235
|
startLiveCall() {
|
|
236
236
|
return __awaiter(this, void 0, void 0, function* () {
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
method: 'POST',
|
|
240
|
-
url: this.doorbotUrl('live_call'),
|
|
241
|
-
})
|
|
242
|
-
.catch((e) => {
|
|
243
|
-
var _a;
|
|
244
|
-
if (((_a = e.response) === null || _a === void 0 ? void 0 : _a.statusCode) === 403) {
|
|
245
|
-
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.`;
|
|
246
|
-
(0, util_1.logError)(errorMessage);
|
|
247
|
-
throw new Error(errorMessage);
|
|
248
|
-
}
|
|
249
|
-
throw e;
|
|
250
|
-
});
|
|
251
|
-
return new live_call_1.LiveCall(liveCall.data.session_id, this);
|
|
252
|
-
});
|
|
253
|
-
}
|
|
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;
|
|
237
|
+
const auth = yield this.restClient.getCurrentAuth();
|
|
238
|
+
return new live_call_1.LiveCall(auth.access_token, this);
|
|
288
239
|
});
|
|
289
240
|
}
|
|
290
241
|
removeDingById(idToRemove) {
|
|
@@ -297,7 +248,6 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
297
248
|
this.onNewDing.next(ding);
|
|
298
249
|
setTimeout(() => {
|
|
299
250
|
this.removeDingById(ding.id_str);
|
|
300
|
-
this.expiredDingIds = this.expiredDingIds.filter((id) => id !== dingId);
|
|
301
251
|
}, 65 * 1000); // dings last ~1 minute
|
|
302
252
|
}
|
|
303
253
|
getEvents(options = {}) {
|
|
@@ -402,7 +352,7 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
402
352
|
getNextSnapshot({ afterMs, maxWaitMs, force, }) {
|
|
403
353
|
return __awaiter(this, void 0, void 0, function* () {
|
|
404
354
|
const response = yield this.restClient.request({
|
|
405
|
-
url: `https://app-snaps.ring.com/snapshots/next/${this.id}
|
|
355
|
+
url: `https://app-snaps.ring.com/snapshots/next/${this.id}`,
|
|
406
356
|
responseType: 'buffer',
|
|
407
357
|
searchParams: {
|
|
408
358
|
'after-ms': afterMs,
|
|
@@ -418,49 +368,9 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
418
368
|
return response;
|
|
419
369
|
});
|
|
420
370
|
}
|
|
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
371
|
recordToFile(outputPath, duration = 30) {
|
|
461
372
|
return __awaiter(this, void 0, void 0, function* () {
|
|
462
|
-
const liveCall = yield this.
|
|
463
|
-
yield liveCall.startTranscoding({
|
|
373
|
+
const liveCall = yield this.streamVideo({
|
|
464
374
|
output: ['-t', duration.toString(), outputPath],
|
|
465
375
|
});
|
|
466
376
|
yield (0, rxjs_1.firstValueFrom)(liveCall.onCallEnded);
|
|
@@ -468,9 +378,9 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
468
378
|
}
|
|
469
379
|
streamVideo(ffmpegOptions) {
|
|
470
380
|
return __awaiter(this, void 0, void 0, function* () {
|
|
471
|
-
const
|
|
472
|
-
yield
|
|
473
|
-
return
|
|
381
|
+
const liveCall = yield this.startLiveCall();
|
|
382
|
+
yield liveCall.startTranscoding(ffmpegOptions);
|
|
383
|
+
return liveCall;
|
|
474
384
|
});
|
|
475
385
|
}
|
|
476
386
|
subscribeToDingEvents() {
|
|
@@ -502,5 +412,3 @@ class RingCamera extends subscribed_1.Subscribed {
|
|
|
502
412
|
}
|
|
503
413
|
}
|
|
504
414
|
exports.RingCamera = RingCamera;
|
|
505
|
-
// SOMEDAY: extract image from video file?
|
|
506
|
-
// ffmpeg -i input.mp4 -r 1 -f image2 image-%2d.png
|
package/lib/api/ring-types.d.ts
CHANGED
|
@@ -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"
|
|
@@ -378,6 +379,13 @@ export interface CameraData {
|
|
|
378
379
|
live_view_disabled?: boolean;
|
|
379
380
|
motion_detection_enabled?: boolean;
|
|
380
381
|
power_mode?: 'battery' | 'wired';
|
|
382
|
+
sheila_settings: {
|
|
383
|
+
cv_processing_enabled: boolean | null;
|
|
384
|
+
local_storage_enabled: boolean | null;
|
|
385
|
+
};
|
|
386
|
+
server_settings: {
|
|
387
|
+
ring_media_server_enabled: boolean;
|
|
388
|
+
};
|
|
381
389
|
};
|
|
382
390
|
features: {
|
|
383
391
|
motions_enabled: boolean;
|
|
@@ -688,21 +696,6 @@ export interface ActiveDing {
|
|
|
688
696
|
sip_token: string;
|
|
689
697
|
sip_ding_id: string;
|
|
690
698
|
}
|
|
691
|
-
export interface LiveCallResponse {
|
|
692
|
-
ding_id: string;
|
|
693
|
-
ding_kind: DingKind;
|
|
694
|
-
device_id: number;
|
|
695
|
-
device_kind: RingCameraKind;
|
|
696
|
-
device_description: string;
|
|
697
|
-
state: DingState;
|
|
698
|
-
protocol: 'ring_media_server' | unknown;
|
|
699
|
-
now: number;
|
|
700
|
-
video_jitter_buffer_ms: number;
|
|
701
|
-
audio_jitter_buffer_ms: number;
|
|
702
|
-
data: {
|
|
703
|
-
session_id: string;
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
699
|
export interface AuthTokenResponse {
|
|
707
700
|
access_token: string;
|
|
708
701
|
expires_in: number;
|
package/lib/api/ring-types.js
CHANGED
|
@@ -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',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ring-client-api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0-beta.2",
|
|
4
4
|
"description": "Unofficial API for Ring doorbells, cameras, security alarm system and smart lighting",
|
|
5
5
|
"main": "lib/api/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"watch:tests": "jest --watch",
|
|
15
15
|
"lint": "eslint '**/*.ts' --fix",
|
|
16
16
|
"build": "rm -rf lib && tsc --declaration",
|
|
17
|
-
"build-quick": "rm -rf lib && esbuild ./{api,examples,homebridge}/*.ts
|
|
17
|
+
"build-quick": "rm -rf lib && esbuild ./{api,examples,homebridge}/*.ts --target=es2017 --format=cjs --outdir=lib",
|
|
18
18
|
"example": "npm run build-quick && node ./lib/examples/example.js",
|
|
19
19
|
"api-example": "npm run build-quick && node ./lib/examples/api-example.js",
|
|
20
20
|
"record-example": "npm run build-quick && node ./lib/examples/record-example.js",
|
|
@@ -57,15 +57,13 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@homebridge/camera-utils": "2.1.0",
|
|
59
59
|
"@homebridge/plugin-ui-utils": "0.0.19",
|
|
60
|
-
"@koush/werift": "0.14.5-
|
|
60
|
+
"@koush/werift": "0.14.5-beta7",
|
|
61
61
|
"@types/socket.io-client": "1.4.36",
|
|
62
62
|
"colors": "1.4.0",
|
|
63
63
|
"debug": "4.3.3",
|
|
64
64
|
"got": "11.8.3",
|
|
65
65
|
"rxjs": "7.5.2",
|
|
66
|
-
"sdp": "3.0.3",
|
|
67
66
|
"socket.io-client": "2.4.0",
|
|
68
|
-
"stun": "2.1.0",
|
|
69
67
|
"systeminformation": "5.10.7",
|
|
70
68
|
"uuid": "8.3.2",
|
|
71
69
|
"ws": "8.4.2"
|
|
@@ -97,8 +95,8 @@
|
|
|
97
95
|
"typescript": "4.5.5"
|
|
98
96
|
},
|
|
99
97
|
"engines": {
|
|
100
|
-
"node": "^
|
|
101
|
-
"homebridge": ">=1.
|
|
98
|
+
"node": "^12.20 || ^14 || ^16",
|
|
99
|
+
"homebridge": ">=1.4.0"
|
|
102
100
|
},
|
|
103
101
|
"bugs": {
|
|
104
102
|
"url": "https://github.com/dgreif/ring/issues"
|
package/lib/api/rtp-utils.d.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { RtpSplitter, SrtpOptions } from '@homebridge/camera-utils';
|
|
3
|
-
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
|
-
export interface LiveCallSession {
|
|
32
|
-
alexa_port: 18443;
|
|
33
|
-
app_session_token: string;
|
|
34
|
-
availability_zone: 'availability-zone';
|
|
35
|
-
custom_timer: {
|
|
36
|
-
max_sec: number;
|
|
37
|
-
};
|
|
38
|
-
ding_id: string;
|
|
39
|
-
ding_kind: DingKind;
|
|
40
|
-
doorbot_id: number;
|
|
41
|
-
exp: number;
|
|
42
|
-
ip: string;
|
|
43
|
-
port: number;
|
|
44
|
-
private_ip: string;
|
|
45
|
-
rms_fqdn: string;
|
|
46
|
-
rms_version: string;
|
|
47
|
-
rsp_port: number;
|
|
48
|
-
rtsp_port: number;
|
|
49
|
-
session_id: string;
|
|
50
|
-
sip_port: number;
|
|
51
|
-
webrtc_port: number;
|
|
52
|
-
webrtc_url: string;
|
|
53
|
-
wwr_port: number;
|
|
54
|
-
}
|
|
55
|
-
export declare function parseLiveCallSession(sessionId: string): LiveCallSession;
|
package/lib/api/rtp-utils.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
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;
|
|
57
|
-
function parseLiveCallSession(sessionId) {
|
|
58
|
-
const encodedSession = sessionId.split('.')[1], buff = new Buffer(encodedSession, 'base64'), text = buff.toString('ascii');
|
|
59
|
-
return JSON.parse(text);
|
|
60
|
-
}
|
|
61
|
-
exports.parseLiveCallSession = parseLiveCallSession;
|
package/lib/api/sip-call.d.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { Subject } from 'rxjs';
|
|
2
|
-
import { RtpDescription, RtpOptions } from './rtp-utils';
|
|
3
|
-
export declare const expiredDingError: Error;
|
|
4
|
-
interface UriOptions {
|
|
5
|
-
name?: string;
|
|
6
|
-
uri: string;
|
|
7
|
-
params?: {
|
|
8
|
-
tag?: string;
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
interface SipHeaders {
|
|
12
|
-
[name: string]: string | any;
|
|
13
|
-
cseq: {
|
|
14
|
-
seq: number;
|
|
15
|
-
method: string;
|
|
16
|
-
};
|
|
17
|
-
to: UriOptions;
|
|
18
|
-
from: UriOptions;
|
|
19
|
-
contact?: UriOptions[];
|
|
20
|
-
via?: UriOptions[];
|
|
21
|
-
}
|
|
22
|
-
export interface SipRequest {
|
|
23
|
-
uri: UriOptions | string;
|
|
24
|
-
method: string;
|
|
25
|
-
headers: SipHeaders;
|
|
26
|
-
content: string;
|
|
27
|
-
}
|
|
28
|
-
export interface SipResponse {
|
|
29
|
-
status: number;
|
|
30
|
-
reason: string;
|
|
31
|
-
headers: SipHeaders;
|
|
32
|
-
content: string;
|
|
33
|
-
}
|
|
34
|
-
export interface SipClient {
|
|
35
|
-
send: (request: SipRequest | SipResponse, handler?: (response: SipResponse) => void) => void;
|
|
36
|
-
destroy: () => void;
|
|
37
|
-
makeResponse: (response: SipRequest, status: number, method: string) => SipResponse;
|
|
38
|
-
}
|
|
39
|
-
export interface SipOptions {
|
|
40
|
-
to: string;
|
|
41
|
-
from: string;
|
|
42
|
-
dingId: string;
|
|
43
|
-
localIp: string;
|
|
44
|
-
}
|
|
45
|
-
export declare class SipCall {
|
|
46
|
-
private sipOptions;
|
|
47
|
-
private seq;
|
|
48
|
-
private fromParams;
|
|
49
|
-
private toParams;
|
|
50
|
-
private callId;
|
|
51
|
-
private sipClient;
|
|
52
|
-
readonly onEndedByRemote: Subject<unknown>;
|
|
53
|
-
private destroyed;
|
|
54
|
-
private cameraConnected?;
|
|
55
|
-
private cameraConnectedPromise;
|
|
56
|
-
readonly sdp: string;
|
|
57
|
-
readonly audioUfrag: string;
|
|
58
|
-
readonly videoUfrag: string;
|
|
59
|
-
constructor(sipOptions: SipOptions, rtpOptions: RtpOptions, tlsPort: number);
|
|
60
|
-
request({ method, headers, content, seq, }: {
|
|
61
|
-
method: string;
|
|
62
|
-
headers?: Partial<SipHeaders>;
|
|
63
|
-
content?: string;
|
|
64
|
-
seq?: number;
|
|
65
|
-
}): Promise<SipResponse>;
|
|
66
|
-
private ackWithInfo;
|
|
67
|
-
sendDtmf(key: string): Promise<SipResponse>;
|
|
68
|
-
private sendKeyFrameRequest;
|
|
69
|
-
invite(): Promise<RtpDescription>;
|
|
70
|
-
requestKeyFrame(): Promise<void>;
|
|
71
|
-
private speakerActivated;
|
|
72
|
-
activateCameraSpeaker(): Promise<void>;
|
|
73
|
-
sendBye(): Promise<void | SipResponse>;
|
|
74
|
-
destroy(): void;
|
|
75
|
-
}
|
|
76
|
-
export {};
|