homebridge-ring 11.0.1 → 11.0.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 +11 -0
- package/lib/api/streaming/peer-connection.js +6 -0
- package/lib/api/streaming/streaming-connection-base.d.ts +3 -2
- package/lib/api/streaming/streaming-connection-base.js +31 -8
- package/lib/api/streaming/streaming-session.d.ts +9 -2
- package/lib/api/streaming/streaming-session.js +68 -12
- package/lib/homebridge/camera-source.js +16 -18
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
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
|
+
### [11.0.2](https://github.com/dgreif/ring/compare/v11.0.1...v11.0.2) (2022-05-22)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **api:** prevent duplicate events from `onCallEnded` ([14ee6eb](https://github.com/dgreif/ring/commit/14ee6eb90bb6ae568dcfd9e8e176c3001e422f9c)), closes [#941](https://github.com/dgreif/ring/issues/941)
|
|
11
|
+
* handle pcmu audio streams for incoming audio ([06f6ff1](https://github.com/dgreif/ring/commit/06f6ff10ce7c8f2b05b1a12b8f244daa200fa9ab)), closes [#930](https://github.com/dgreif/ring/issues/930)
|
|
12
|
+
* handle pcmu return audio ([7b86182](https://github.com/dgreif/ring/commit/7b8618261a6ccb85b9f760ee1fcc228be00fe5a6)), closes [#930](https://github.com/dgreif/ring/issues/930)
|
|
13
|
+
* **homebride:** use shared engines config to enforce node 16 requirement ([b748cb0](https://github.com/dgreif/ring/commit/b748cb027db0c43ae109e1ea77c6bc83f86e4913))
|
|
14
|
+
* prevent hanging promises from streams that fail to connect ([dd7638e](https://github.com/dgreif/ring/commit/dd7638e32626499c0be501be6b19b5d874fa974f))
|
|
15
|
+
|
|
5
16
|
### [11.0.1](https://github.com/dgreif/ring/compare/v11.0.0...v11.0.1) (2022-05-17)
|
|
6
17
|
|
|
7
18
|
|
|
@@ -31,6 +31,12 @@ class WeriftPeerConnection {
|
|
|
31
31
|
clockRate: 48000,
|
|
32
32
|
channels: 2,
|
|
33
33
|
}),
|
|
34
|
+
new werift_1.RTCRtpCodecParameters({
|
|
35
|
+
mimeType: 'audio/PCMU',
|
|
36
|
+
clockRate: 8000,
|
|
37
|
+
channels: 1,
|
|
38
|
+
payloadType: 0,
|
|
39
|
+
}),
|
|
34
40
|
],
|
|
35
41
|
video: [
|
|
36
42
|
new werift_1.RTCRtpCodecParameters({
|
|
@@ -21,10 +21,11 @@ export declare abstract class StreamingConnectionBase extends Subscribed {
|
|
|
21
21
|
}): Promise<void>;
|
|
22
22
|
protected abstract sendSessionMessage(method: string, body?: Record<any, any>): void;
|
|
23
23
|
private activated;
|
|
24
|
-
activate():
|
|
25
|
-
activateCameraSpeaker():
|
|
24
|
+
activate(): void;
|
|
25
|
+
activateCameraSpeaker(): void;
|
|
26
26
|
protected sendMessage(message: Record<any, any>): void;
|
|
27
27
|
sendAudioPacket(rtp: RtpPacket): void;
|
|
28
|
+
private hasEnded;
|
|
28
29
|
protected callEnded(): void;
|
|
29
30
|
stop(): void;
|
|
30
31
|
}
|
|
@@ -14,6 +14,7 @@ class StreamingConnectionBase extends subscribed_1.Subscribed {
|
|
|
14
14
|
this.onCallAnswered = new rxjs_1.ReplaySubject(1);
|
|
15
15
|
this.onCallEnded = new rxjs_1.ReplaySubject(1);
|
|
16
16
|
this.activated = false;
|
|
17
|
+
this.hasEnded = false;
|
|
17
18
|
if (options.createPeerConnection) {
|
|
18
19
|
// we were passed a custom peer connection factory
|
|
19
20
|
this.pc = options.createPeerConnection();
|
|
@@ -50,25 +51,43 @@ class StreamingConnectionBase extends subscribed_1.Subscribed {
|
|
|
50
51
|
}
|
|
51
52
|
}));
|
|
52
53
|
}
|
|
53
|
-
|
|
54
|
+
activate() {
|
|
54
55
|
if (this.activated) {
|
|
55
56
|
return;
|
|
56
57
|
}
|
|
57
58
|
this.activated = true;
|
|
58
|
-
|
|
59
|
-
(0,
|
|
60
|
-
|
|
59
|
+
// Fire and forget this call so that callers don't get hung up waiting for connection (which might not happen)
|
|
60
|
+
(0, rxjs_1.firstValueFrom)(this.pc.onConnectionState.pipe((0, operators_1.filter)((state) => state === 'connected')))
|
|
61
|
+
.then(() => {
|
|
62
|
+
(0, util_1.logInfo)('Activating Session');
|
|
63
|
+
this.sendSessionMessage('activate_session');
|
|
64
|
+
})
|
|
65
|
+
.catch((e) => {
|
|
66
|
+
(0, util_1.logError)(e);
|
|
67
|
+
});
|
|
61
68
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
69
|
+
activateCameraSpeaker() {
|
|
70
|
+
// Fire and forget this call so that callers don't get hung up waiting for answer (which might not happen)
|
|
71
|
+
(0, rxjs_1.firstValueFrom)(this.onCallAnswered)
|
|
72
|
+
.then(() => {
|
|
73
|
+
this.sendSessionMessage('camera_options', {
|
|
74
|
+
stealth_mode: false,
|
|
75
|
+
});
|
|
76
|
+
})
|
|
77
|
+
.catch((e) => {
|
|
78
|
+
(0, util_1.logError)(e);
|
|
66
79
|
});
|
|
67
80
|
}
|
|
68
81
|
sendMessage(message) {
|
|
82
|
+
if (this.hasEnded) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
69
85
|
this.ws.send(JSON.stringify(message));
|
|
70
86
|
}
|
|
71
87
|
sendAudioPacket(rtp) {
|
|
88
|
+
if (this.hasEnded) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
72
91
|
if (this.pc instanceof peer_connection_1.WeriftPeerConnection) {
|
|
73
92
|
this.pc.returnAudioTrack.writeRtp(rtp);
|
|
74
93
|
}
|
|
@@ -77,6 +96,9 @@ class StreamingConnectionBase extends subscribed_1.Subscribed {
|
|
|
77
96
|
}
|
|
78
97
|
}
|
|
79
98
|
callEnded() {
|
|
99
|
+
if (this.hasEnded) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
80
102
|
try {
|
|
81
103
|
this.sendMessage({
|
|
82
104
|
reason: { code: 0, text: '' },
|
|
@@ -87,6 +109,7 @@ class StreamingConnectionBase extends subscribed_1.Subscribed {
|
|
|
87
109
|
catch (_) {
|
|
88
110
|
// ignore any errors since we are stopping the call
|
|
89
111
|
}
|
|
112
|
+
this.hasEnded = true;
|
|
90
113
|
this.unsubscribe();
|
|
91
114
|
this.onCallEnded.next();
|
|
92
115
|
this.pc.close();
|
|
@@ -15,18 +15,25 @@ export declare class StreamingSession extends Subscribed {
|
|
|
15
15
|
private readonly camera;
|
|
16
16
|
private connection;
|
|
17
17
|
readonly onCallEnded: ReplaySubject<void>;
|
|
18
|
+
private readonly onUsingOpus;
|
|
18
19
|
readonly onVideoRtp: Subject<RtpPacket>;
|
|
19
20
|
readonly onAudioRtp: Subject<RtpPacket>;
|
|
20
21
|
private readonly audioSplitter;
|
|
21
22
|
private readonly videoSplitter;
|
|
23
|
+
private readonly returnAudioSplitter;
|
|
22
24
|
constructor(camera: RingCamera, connection: RingEdgeConnection | WebrtcConnection);
|
|
23
25
|
private bindToConnection;
|
|
24
26
|
activated: boolean;
|
|
25
|
-
activate():
|
|
27
|
+
activate(): void;
|
|
26
28
|
cameraSpeakerActivated: boolean;
|
|
27
|
-
activateCameraSpeaker():
|
|
29
|
+
activateCameraSpeaker(): void;
|
|
28
30
|
reservePort(bufferPorts?: number): Promise<number>;
|
|
31
|
+
get isUsingOpus(): Promise<boolean>;
|
|
29
32
|
startTranscoding(ffmpegOptions: FfmpegOptions): Promise<void>;
|
|
33
|
+
transcodeReturnAudio(ffmpegOptions: {
|
|
34
|
+
input: SpawnInput[];
|
|
35
|
+
}): Promise<void>;
|
|
36
|
+
private hasEnded;
|
|
30
37
|
private callEnded;
|
|
31
38
|
stop(): void;
|
|
32
39
|
sendAudioPacket(rtp: RtpPacket): void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StreamingSession = void 0;
|
|
4
|
+
const werift_1 = require("werift");
|
|
4
5
|
const camera_utils_1 = require("@homebridge/camera-utils");
|
|
5
6
|
const rxjs_1 = require("rxjs");
|
|
6
7
|
const ffmpeg_1 = require("../ffmpeg");
|
|
@@ -21,48 +22,59 @@ class StreamingSession extends subscribed_1.Subscribed {
|
|
|
21
22
|
this.camera = camera;
|
|
22
23
|
this.connection = connection;
|
|
23
24
|
this.onCallEnded = new rxjs_1.ReplaySubject(1);
|
|
25
|
+
this.onUsingOpus = new rxjs_1.ReplaySubject(1);
|
|
24
26
|
this.onVideoRtp = new rxjs_1.Subject();
|
|
25
27
|
this.onAudioRtp = new rxjs_1.Subject();
|
|
26
28
|
this.audioSplitter = new camera_utils_1.RtpSplitter();
|
|
27
29
|
this.videoSplitter = new camera_utils_1.RtpSplitter();
|
|
30
|
+
this.returnAudioSplitter = new camera_utils_1.RtpSplitter();
|
|
28
31
|
this.activated = false;
|
|
29
32
|
this.cameraSpeakerActivated = false;
|
|
33
|
+
this.hasEnded = false;
|
|
30
34
|
this.bindToConnection(connection);
|
|
31
35
|
}
|
|
32
36
|
bindToConnection(connection) {
|
|
33
|
-
this.addSubscriptions(connection.onAudioRtp.subscribe(this.onAudioRtp), connection.onVideoRtp.subscribe(this.onVideoRtp))
|
|
37
|
+
this.addSubscriptions(connection.onAudioRtp.subscribe(this.onAudioRtp), connection.onVideoRtp.subscribe(this.onVideoRtp), connection.onCallAnswered.subscribe((sdp) => {
|
|
38
|
+
this.onUsingOpus.next(sdp.toLocaleLowerCase().includes(' opus/'));
|
|
39
|
+
}));
|
|
34
40
|
}
|
|
35
|
-
|
|
36
|
-
if (this.activated) {
|
|
41
|
+
activate() {
|
|
42
|
+
if (this.activated || this.hasEnded) {
|
|
37
43
|
return;
|
|
38
44
|
}
|
|
39
45
|
this.activated = true;
|
|
40
|
-
|
|
46
|
+
this.connection.activate();
|
|
41
47
|
}
|
|
42
|
-
|
|
43
|
-
if (this.cameraSpeakerActivated) {
|
|
48
|
+
activateCameraSpeaker() {
|
|
49
|
+
if (this.cameraSpeakerActivated || this.hasEnded) {
|
|
44
50
|
return;
|
|
45
51
|
}
|
|
46
52
|
this.cameraSpeakerActivated = true;
|
|
47
|
-
|
|
53
|
+
this.connection.activateCameraSpeaker();
|
|
48
54
|
}
|
|
49
55
|
async reservePort(bufferPorts = 0) {
|
|
50
56
|
const ports = await (0, camera_utils_1.reservePorts)({ count: bufferPorts + 1 });
|
|
51
57
|
return ports[0];
|
|
52
58
|
}
|
|
59
|
+
get isUsingOpus() {
|
|
60
|
+
return (0, rxjs_1.firstValueFrom)(this.onUsingOpus);
|
|
61
|
+
}
|
|
53
62
|
async startTranscoding(ffmpegOptions) {
|
|
54
|
-
|
|
63
|
+
if (this.hasEnded) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const videoPort = await this.reservePort(1), audioPort = await this.reservePort(1), transcodeVideoStream = ffmpegOptions.video !== false, ringSdp = await (0, rxjs_1.firstValueFrom)(this.connection.onCallAnswered), usingOpus = await this.isUsingOpus, ffmpegInputArguments = [
|
|
55
67
|
'-hide_banner',
|
|
56
68
|
'-protocol_whitelist',
|
|
57
69
|
'pipe,udp,rtp,file,crypto',
|
|
58
|
-
|
|
59
|
-
'libopus',
|
|
70
|
+
// Ring will answer with either opus or pcmu
|
|
71
|
+
...(usingOpus ? ['-acodec', 'libopus'] : []),
|
|
60
72
|
'-f',
|
|
61
73
|
'sdp',
|
|
62
74
|
...(ffmpegOptions.input || []),
|
|
63
75
|
'-i',
|
|
64
76
|
'pipe:',
|
|
65
|
-
],
|
|
77
|
+
], inputSdp = getCleanSdp(ringSdp, transcodeVideoStream)
|
|
66
78
|
.replace(/m=audio \d+/, `m=audio ${audioPort}`)
|
|
67
79
|
.replace(/m=video \d+/, `m=video ${videoPort}`), ff = new camera_utils_1.FfmpegProcess({
|
|
68
80
|
ffmpegArgs: ffmpegInputArguments.concat(...(ffmpegOptions.audio || ['-acodec', 'aac']), ...(transcodeVideoStream
|
|
@@ -95,19 +107,63 @@ class StreamingSession extends subscribed_1.Subscribed {
|
|
|
95
107
|
this.onCallEnded.subscribe(() => ff.stop());
|
|
96
108
|
ff.writeStdin(inputSdp);
|
|
97
109
|
// Activate the stream now that ffmpeg is ready to receive
|
|
98
|
-
|
|
110
|
+
this.activate();
|
|
111
|
+
}
|
|
112
|
+
async transcodeReturnAudio(ffmpegOptions) {
|
|
113
|
+
if (this.hasEnded) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const audioOutForwarder = new camera_utils_1.RtpSplitter(({ message }) => {
|
|
117
|
+
const rtp = werift_1.RtpPacket.deSerialize(message);
|
|
118
|
+
this.connection.sendAudioPacket(rtp);
|
|
119
|
+
return null;
|
|
120
|
+
}), usingOpus = await this.isUsingOpus, ff = new camera_utils_1.FfmpegProcess({
|
|
121
|
+
ffmpegArgs: [
|
|
122
|
+
'-hide_banner',
|
|
123
|
+
'-protocol_whitelist',
|
|
124
|
+
'pipe,udp,rtp,file,crypto',
|
|
125
|
+
'-re',
|
|
126
|
+
'-i',
|
|
127
|
+
...ffmpegOptions.input,
|
|
128
|
+
'-acodec',
|
|
129
|
+
...(usingOpus
|
|
130
|
+
? ['libopus', '-ac', 2, '-ar', '48k']
|
|
131
|
+
: ['pcm_mulaw', '-ac', 1, '-ar', '8k']),
|
|
132
|
+
'-flags',
|
|
133
|
+
'+global_header',
|
|
134
|
+
'-f',
|
|
135
|
+
'rtp',
|
|
136
|
+
`rtp://127.0.0.1:${await audioOutForwarder.portPromise}`,
|
|
137
|
+
],
|
|
138
|
+
ffmpegPath: (0, ffmpeg_1.getFfmpegPath)(),
|
|
139
|
+
exitCallback: () => this.callEnded(),
|
|
140
|
+
logLabel: `Return Audio (${this.camera.name})`,
|
|
141
|
+
logger: {
|
|
142
|
+
error: util_1.logError,
|
|
143
|
+
info: util_1.logDebug,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
this.onCallEnded.subscribe(() => ff.stop());
|
|
99
147
|
}
|
|
100
148
|
callEnded() {
|
|
149
|
+
if (this.hasEnded) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.hasEnded = true;
|
|
101
153
|
this.unsubscribe();
|
|
102
154
|
this.onCallEnded.next();
|
|
103
155
|
this.connection.stop();
|
|
104
156
|
this.audioSplitter.close();
|
|
105
157
|
this.videoSplitter.close();
|
|
158
|
+
this.returnAudioSplitter.close();
|
|
106
159
|
}
|
|
107
160
|
stop() {
|
|
108
161
|
this.callEnded();
|
|
109
162
|
}
|
|
110
163
|
sendAudioPacket(rtp) {
|
|
164
|
+
if (this.hasEnded) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
111
167
|
this.connection.sendAudioPacket(rtp);
|
|
112
168
|
}
|
|
113
169
|
}
|
|
@@ -26,8 +26,8 @@ function getSessionConfig(srtpOptions) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
class StreamingSessionWrapper {
|
|
29
|
-
constructor(
|
|
30
|
-
this.
|
|
29
|
+
constructor(streamingSession, prepareStreamRequest, ringCamera, start) {
|
|
30
|
+
this.streamingSession = streamingSession;
|
|
31
31
|
this.prepareStreamRequest = prepareStreamRequest;
|
|
32
32
|
this.ringCamera = ringCamera;
|
|
33
33
|
this.start = start;
|
|
@@ -63,15 +63,15 @@ class StreamingSessionWrapper {
|
|
|
63
63
|
onReturnPacketReceived.next(null);
|
|
64
64
|
return null;
|
|
65
65
|
});
|
|
66
|
-
|
|
66
|
+
streamingSession.addSubscriptions((0, rxjs_1.merge)((0, rxjs_1.of)(true).pipe((0, operators_1.delay)(15000)), onReturnPacketReceived)
|
|
67
67
|
.pipe((0, operators_1.debounceTime)(5000))
|
|
68
68
|
.subscribe(() => {
|
|
69
69
|
(0, util_1.logInfo)(`Live stream for ${this.ringCamera.name} appears to be inactive. (${getDurationSeconds(start)}s)`);
|
|
70
|
-
|
|
70
|
+
streamingSession.stop();
|
|
71
71
|
}));
|
|
72
72
|
// Periodically send a blank RTCP packet to the HomeKit video port
|
|
73
73
|
// Without this, HomeKit assumes the stream is dead after 30 second and sends a stop request
|
|
74
|
-
|
|
74
|
+
streamingSession.addSubscriptions((0, rxjs_1.interval)(500).subscribe(() => {
|
|
75
75
|
const senderInfo = new werift_1.RtcpSenderInfo({
|
|
76
76
|
ntpTimestamp: BigInt(0),
|
|
77
77
|
packetCount: 0,
|
|
@@ -95,7 +95,7 @@ class StreamingSessionWrapper {
|
|
|
95
95
|
// use to encrypt Ring video to HomeKit
|
|
96
96
|
videoSrtpSession = new werift_1.SrtpSession(getSessionConfig(this.videoSrtp));
|
|
97
97
|
// Set up packet forwarding for video stream
|
|
98
|
-
this.
|
|
98
|
+
this.streamingSession.addSubscriptions(this.streamingSession.onVideoRtp.subscribe(({ header, payload }) => {
|
|
99
99
|
header.ssrc = this.videoSsrc;
|
|
100
100
|
header.payloadType = request.video.pt;
|
|
101
101
|
const encryptedPacket = videoSrtpSession.encrypt(payload, header);
|
|
@@ -112,9 +112,9 @@ class StreamingSessionWrapper {
|
|
|
112
112
|
}));
|
|
113
113
|
const shouldTranscodeAudio = await this.libfdkAacInstalledPromise;
|
|
114
114
|
if (!shouldTranscodeAudio) {
|
|
115
|
-
return this.
|
|
115
|
+
return this.streamingSession.activate();
|
|
116
116
|
}
|
|
117
|
-
const transcodingPromise = this.
|
|
117
|
+
const transcodingPromise = this.streamingSession.startTranscoding({
|
|
118
118
|
input: ['-vn'],
|
|
119
119
|
audio: [
|
|
120
120
|
'-map',
|
|
@@ -167,20 +167,20 @@ class StreamingSessionWrapper {
|
|
|
167
167
|
}, audioSrtpSession = new werift_1.SrtpSession(getSessionConfig(remoteAudioSrtpOptions)), returnAudioTranscodedSplitter = new camera_utils_1.RtpSplitter(({ message }) => {
|
|
168
168
|
if (!cameraSpeakerActive) {
|
|
169
169
|
cameraSpeakerActive = true;
|
|
170
|
-
this.
|
|
170
|
+
this.streamingSession.activateCameraSpeaker();
|
|
171
171
|
}
|
|
172
172
|
// decrypt the message
|
|
173
173
|
try {
|
|
174
174
|
const rtp = werift_1.RtpPacket.deSerialize(message);
|
|
175
175
|
rtp.payload = audioSrtpSession.decrypt(rtp.payload);
|
|
176
176
|
// send to Ring - werift will handle encryption and other header params
|
|
177
|
-
this.
|
|
177
|
+
this.streamingSession.sendAudioPacket(rtp);
|
|
178
178
|
}
|
|
179
179
|
catch (_) {
|
|
180
180
|
// deSerialize will sometimes fail, but the errors can be ignored
|
|
181
181
|
}
|
|
182
182
|
return null;
|
|
183
|
-
}), returnAudioTranscoder = new camera_utils_1.ReturnAudioTranscoder({
|
|
183
|
+
}), usingOpus = await this.streamingSession.isUsingOpus, returnAudioTranscoder = new camera_utils_1.ReturnAudioTranscoder({
|
|
184
184
|
prepareStreamRequest: this.prepareStreamRequest,
|
|
185
185
|
incomingAudioOptions: {
|
|
186
186
|
ssrc: this.audioSsrc,
|
|
@@ -188,13 +188,11 @@ class StreamingSessionWrapper {
|
|
|
188
188
|
},
|
|
189
189
|
outputArgs: [
|
|
190
190
|
'-acodec',
|
|
191
|
-
|
|
191
|
+
...(usingOpus
|
|
192
|
+
? ['libopus', '-ac', 2, '-ar', '48k']
|
|
193
|
+
: ['pcm_mulaw', '-ac', 1, '-ar', '8k']),
|
|
192
194
|
'-flags',
|
|
193
195
|
'+global_header',
|
|
194
|
-
'-ac',
|
|
195
|
-
2,
|
|
196
|
-
'-ar',
|
|
197
|
-
'48k',
|
|
198
196
|
'-f',
|
|
199
197
|
'rtp',
|
|
200
198
|
`rtp://127.0.0.1:${await returnAudioTranscodedSplitter.portPromise}`,
|
|
@@ -207,7 +205,7 @@ class StreamingSessionWrapper {
|
|
|
207
205
|
logLabel: `Return Audio (${this.ringCamera.name})`,
|
|
208
206
|
returnAudioSplitter: this.audioSplitter,
|
|
209
207
|
});
|
|
210
|
-
this.
|
|
208
|
+
this.streamingSession.onCallEnded.pipe((0, operators_1.take)(1)).subscribe(() => {
|
|
211
209
|
returnAudioTranscoder.stop();
|
|
212
210
|
returnAudioTranscodedSplitter.close();
|
|
213
211
|
});
|
|
@@ -217,7 +215,7 @@ class StreamingSessionWrapper {
|
|
|
217
215
|
stop() {
|
|
218
216
|
this.audioSplitter.close();
|
|
219
217
|
this.videoSplitter.close();
|
|
220
|
-
this.
|
|
218
|
+
this.streamingSession.stop();
|
|
221
219
|
}
|
|
222
220
|
}
|
|
223
221
|
class CameraSource {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-ring",
|
|
3
|
-
"version": "11.0.
|
|
3
|
+
"version": "11.0.2",
|
|
4
4
|
"description": "Homebridge plugin for Ring doorbells, cameras, security alarm system and smart lighting",
|
|
5
5
|
"main": "lib/homebridge/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@eneris/push-receiver": "^3.0.1",
|
|
43
43
|
"@homebridge/camera-utils": "^2.1.0",
|
|
44
44
|
"@homebridge/plugin-ui-utils": "^0.0.19",
|
|
45
|
-
"werift": "^0.15.
|
|
45
|
+
"werift": "^0.15.1",
|
|
46
46
|
"@types/socket.io-client": "1.4.36",
|
|
47
47
|
"colors": "^1.4.0",
|
|
48
48
|
"debug": "^4.3.4",
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"typescript": "4.6.4"
|
|
82
82
|
},
|
|
83
83
|
"engines": {
|
|
84
|
-
"node": "
|
|
85
|
-
"homebridge": ">=
|
|
84
|
+
"node": "^16",
|
|
85
|
+
"homebridge": ">=1.4.0"
|
|
86
86
|
},
|
|
87
87
|
"bugs": {
|
|
88
88
|
"url": "https://github.com/dgreif/ring/issues"
|