Skip to content

Commit 5b92aea

Browse files
committed
webrtc: fix google device access leaks?
1 parent 93f8f43 commit 5b92aea

File tree

5 files changed

+68
-26
lines changed

5 files changed

+68
-26
lines changed

plugins/webrtc/package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/webrtc/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@scrypted/webrtc",
3-
"version": "0.2.24",
3+
"version": "0.2.25",
44
"scripts": {
55
"scrypted-setup-project": "scrypted-setup-project",
66
"prescrypted-setup-project": "scrypted-package-json",

plugins/webrtc/src/main.ts

+38-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { createBrowserSignalingSession } from "@scrypted/common/src/rtc-connect"
66
import { legacyGetSignalingSessionOptions } from '@scrypted/common/src/rtc-signaling';
77
import { SettingsMixinDeviceBase, SettingsMixinDeviceOptions } from '@scrypted/common/src/settings-mixin';
88
import { createZygote } from '@scrypted/common/src/zygote';
9-
import sdk, { BufferConverter, ConnectOptions, DeviceCreator, DeviceCreatorSettings, DeviceProvider, FFmpegInput, HttpRequest, Intercom, MediaObject, MediaObjectOptions, MixinProvider, RTCSessionControl, RTCSignalingChannel, RTCSignalingClient, RTCSignalingOptions, RTCSignalingSession, RequestMediaStream, RequestMediaStreamOptions, ResponseMediaStreamOptions, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, SettingValue, Settings, VideoCamera, WritableDeviceState } from '@scrypted/sdk';
9+
import sdk, { BufferConverter, ConnectOptions, DeviceCreator, DeviceCreatorSettings, DeviceProvider, FFmpegInput, HttpRequest, Intercom, MediaObject, MediaObjectOptions, MixinProvider, RTCSessionControl, RTCSignalingChannel, RTCSignalingClient, RTCSignalingOptions, RTCSignalingSession, RequestMediaStream, RequestMediaStreamOptions, ResponseMediaStreamOptions, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, ScryptedNativeId, Setting, SettingValue, Settings, VideoCamera, WritableDeviceState } from '@scrypted/sdk';
1010
import { StorageSettings } from '@scrypted/sdk/storage-settings';
1111
import crypto from 'crypto';
1212
import ip from 'ip';
@@ -19,7 +19,7 @@ import { waitClosed } from './peerconnection-util';
1919
import { WebRTCCamera } from "./webrtc-camera";
2020
import { MediaStreamTrack, PeerConfig, RTCPeerConnection, defaultPeerConfig } from './werift';
2121
import { WeriftSignalingSession } from './werift-signaling-session';
22-
import { createRTCPeerConnectionSource, getRTCMediaStreamOptions } from './wrtc-to-rtsp';
22+
import { RTCPeerConnectionPipe, createRTCPeerConnectionSource, getRTCMediaStreamOptions } from './wrtc-to-rtsp';
2323

2424
const { mediaManager, systemManager, deviceManager } = sdk;
2525

@@ -154,15 +154,29 @@ class WebRTCMixin extends SettingsMixinDeviceBase<RTCSignalingClient & VideoCame
154154
return this.mixinDevice.getVideoStream(options);
155155
}
156156

157-
const { intercom, mediaObject, pcClose } = await createRTCPeerConnectionSource({
158-
console: this.console,
157+
const result = zygote();
158+
this.plugin.activeConnections++;
159+
result.worker.on('exit', () => {
160+
this.plugin.activeConnections--;
161+
});
162+
163+
const fork = await result.result;
164+
165+
const { getIntercom, mediaObject, pcClose } = await fork.createRTCPeerConnectionSource({
166+
__json_copy_serialize_children: true,
167+
nativeId: this.nativeId,
168+
mixinId: this.id,
159169
mediaStreamOptions: this.createVideoStreamOptions(),
160-
channel: this.mixinDevice,
170+
startRTCSignalingSession: (session) => this.mixinDevice.startRTCSignalingSession(session),
161171
maximumCompatibilityMode: this.plugin.storageSettings.values.maximumCompatibilityMode,
162172
});
163173

164-
this.webrtcIntercom = intercom;
165-
pcClose.finally(() => this.webrtcIntercom = undefined);
174+
this.webrtcIntercom = getIntercom();
175+
const pcc = pcClose();
176+
pcc.finally(() => {
177+
this.webrtcIntercom = undefined;
178+
result.worker.terminate();
179+
});
166180

167181
return mediaObject;
168182
}
@@ -594,6 +608,23 @@ function handleCleanupConnection(cleanup: Deferred<string>, connection: WebRTCCo
594608

595609
export async function fork() {
596610
return {
611+
async createRTCPeerConnectionSource(options: {
612+
__json_copy_serialize_children: true,
613+
mixinId: string,
614+
nativeId: ScryptedNativeId,
615+
mediaStreamOptions: ResponseMediaStreamOptions,
616+
startRTCSignalingSession: (session: RTCSignalingSession) => Promise<RTCSessionControl | undefined>,
617+
maximumCompatibilityMode: boolean,
618+
}): Promise<RTCPeerConnectionPipe> {
619+
return createRTCPeerConnectionSource({
620+
nativeId: this.nativeId,
621+
mixinId: options.mixinId,
622+
mediaStreamOptions: options.mediaStreamOptions,
623+
startRTCSignalingSession: (session) => options.startRTCSignalingSession(session),
624+
maximumCompatibilityMode: options.maximumCompatibilityMode,
625+
});
626+
},
627+
597628
async createConnection(message: any,
598629
port: number,
599630
clientSession: RTCSignalingSession,

plugins/webrtc/src/webrtc-camera.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,17 @@ export class WebRTCCamera extends ScryptedDeviceBase implements VideoCamera, RTC
2929
async getVideoStream(options?: RequestMediaStreamOptions): Promise<MediaObject> {
3030
const mediaStreamOptions = getRTCMediaStreamOptions('webrtc', 'WebRTC');
3131

32-
const { mediaObject, intercom } = await createRTCPeerConnectionSource({
33-
console: this.console,
32+
// todo: sdk.fork
33+
const { mediaObject, getIntercom } = await createRTCPeerConnectionSource({
34+
mixinId: undefined,
35+
nativeId: this.nativeId,
3436
mediaStreamOptions,
35-
channel: this,
37+
startRTCSignalingSession: session => this.startRTCSignalingSession(session),
3638
maximumCompatibilityMode: this.plugin.storageSettings.values.maximumCompatibilityMode,
3739
});
3840

3941
this.intercom?.then(intercom => intercom.stopIntercom());
40-
this.intercom = intercom;
42+
this.intercom = getIntercom();
4143

4244
return mediaObject;
4345
}

plugins/webrtc/src/wrtc-to-rtsp.ts

+21-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Deferred } from "@scrypted/common/src/deferred";
22
import { listenZeroSingleClient } from "@scrypted/common/src/listen-cluster";
33
import { getNaluTypesInNalu, RtspServer } from "@scrypted/common/src/rtsp-server";
44
import { createSdpInput, parseSdp } from '@scrypted/common/src/sdp-utils';
5-
import sdk, { FFmpegInput, Intercom, MediaObject, MediaStreamUrl, ResponseMediaStreamOptions, RTCAVSignalingSetup, RTCSessionControl, RTCSignalingChannel, RTCSignalingOptions, RTCSignalingSendIceCandidate, RTCSignalingSession, ScryptedMimeTypes } from "@scrypted/sdk";
5+
import sdk, { FFmpegInput, Intercom, MediaObject, MediaStreamUrl, ResponseMediaStreamOptions, RTCAVSignalingSetup, RTCSessionControl, RTCSignalingChannel, RTCSignalingOptions, RTCSignalingSendIceCandidate, RTCSignalingSession, ScryptedMimeTypes, ScryptedNativeId } from "@scrypted/sdk";
66
import { FullIntraRequest } from "../../../external/werift/packages/rtp/src/rtcp/psfb/fullIntraRequest";
77
import { logConnectionState, waitClosed, waitConnected, waitIceConnected } from "./peerconnection-util";
88
import { startRtpForwarderProcess } from "./rtp-forwarders";
@@ -13,9 +13,10 @@ import { createRawResponse, getWeriftIceServers, isPeerConnectionAlive, logIsLoc
1313
const { mediaManager } = sdk;
1414

1515
export interface RTCPeerConnectionPipe {
16+
__json_copy_serialize_children: true,
1617
mediaObject: MediaObject;
17-
intercom: Promise<Intercom>;
18-
pcClose: Promise<unknown>;
18+
getIntercom(): Promise<Intercom>;
19+
pcClose(): Promise<unknown>;
1920
}
2021

2122
function ignoreDeferred(...d: Deferred<any>[]) {
@@ -27,12 +28,14 @@ function ignorePromise(...p: Promise<any>[]) {
2728
}
2829

2930
export async function createRTCPeerConnectionSource(options: {
30-
console: Console,
31+
mixinId: string,
32+
nativeId: ScryptedNativeId,
3133
mediaStreamOptions: ResponseMediaStreamOptions,
32-
channel: RTCSignalingChannel,
34+
startRTCSignalingSession: (session: RTCSignalingSession) => Promise<RTCSessionControl | undefined>,
3335
maximumCompatibilityMode: boolean,
3436
}): Promise<RTCPeerConnectionPipe> {
35-
const { mediaStreamOptions, channel, console, maximumCompatibilityMode } = options;
37+
const { mediaStreamOptions, startRTCSignalingSession, mixinId, nativeId, maximumCompatibilityMode } = options;
38+
const console = mixinId ? sdk.deviceManager.getMixinConsole(mixinId, nativeId) : sdk.deviceManager.getDeviceConsole(nativeId);
3639

3740
const { clientPromise, port } = await listenZeroSingleClient();
3841

@@ -45,9 +48,9 @@ export async function createRTCPeerConnectionSource(options: {
4548

4649
const cleanup = () => {
4750
console.log('webrtc/rtsp cleaning up');
48-
clientPromise.then(client => client.destroy()).catch(() => {});
49-
sessionControl.promise.then(sc => sc.endSession()).catch(() => {});
50-
peerConnection.promise.then(pc => pc.close()).catch(() => {});
51+
clientPromise.then(client => client.destroy()).catch(() => { });
52+
sessionControl.promise.then(sc => sc.endSession()).catch(() => { });
53+
peerConnection.promise.then(pc => pc.close()).catch(() => { });
5154
ignorePromise(intercom.promise.then(intercom => intercom.stopIntercom()));
5255
};
5356

@@ -74,6 +77,8 @@ export async function createRTCPeerConnectionSource(options: {
7477
iceServers: getWeriftIceServers(setup.configuration),
7578
});
7679

80+
waitClosed(ret).then(() => cleanup());
81+
7782
logConnectionState(console, ret);
7883
peerConnection.resolve(ret);
7984

@@ -101,6 +106,8 @@ export async function createRTCPeerConnectionSource(options: {
101106
let gotVideo = false;
102107

103108
const pc = await peerConnection.promise;
109+
const timeout = setTimeout(() => cleanup(), 2 * 60 * 1000);
110+
waitClosed(pc).then(() => clearTimeout(timeout));
104111

105112
const setupAudioTranscevier = (transciever: RTCRtpTransceiver) => {
106113
audioTransceiver = transciever;
@@ -135,6 +142,7 @@ export async function createRTCPeerConnectionSource(options: {
135142
track.onReceiveRtcp.subscribe(rtp => rtspServer.sendTrack(videoTrack, rtp.serialize(), true));
136143

137144
track.onReceiveRtp.once(() => {
145+
clearTimeout(timeout);
138146
let firSequenceNumber = 0;
139147
const pictureLossInterval = setInterval(() => {
140148
// i think this is necessary for older clients like ring
@@ -266,7 +274,7 @@ export async function createRTCPeerConnectionSource(options: {
266274
}
267275

268276
const session = new SignalingSession();
269-
const sc = await channel.startRTCSignalingSession(session);
277+
const sc = await startRTCSignalingSession(session);
270278
sessionControl.resolve(sc);
271279
console.log('waiting for peer connection');
272280
const pc = await peerConnection.promise;
@@ -351,9 +359,10 @@ export async function createRTCPeerConnectionSource(options: {
351359
};
352360

353361
return {
362+
__json_copy_serialize_children: true,
354363
mediaObject: await mediaManager.createMediaObject(mediaStreamUrl, ScryptedMimeTypes.MediaStreamUrl),
355-
intercom: intercom.promise,
356-
pcClose,
364+
getIntercom: () => intercom.promise,
365+
pcClose: () => pcClose,
357366
};
358367
}
359368

0 commit comments

Comments
 (0)