From b1eef486814600b4349988141dc8558f3569b4b1 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Thu, 12 Jan 2023 17:56:33 +0100 Subject: [PATCH 01/21] Fix fontawesome (#509) * * Fix an issues in SIP.js where the ACK and BYE replies didn't go to the correct uri * Fix @fortawesome/fontawesome fonts in package-lock.json pointing to wrong registry --- plugins/bticino/src/main.ts | 2 +- plugins/core/ui/package-lock.json | 50 +++++++++++++------------ plugins/sip/src/main.ts | 2 +- plugins/sip/src/sip-call.ts | 61 ++++++++++++++++++------------- plugins/sip/src/sip-session.ts | 1 - 5 files changed, 63 insertions(+), 53 deletions(-) diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 5e5e0d582..328570c99 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -326,7 +326,7 @@ export class SipCamProvider extends ScryptedDeviceBase implements DeviceProvider } } - async releaseDevice(id: string, nativeId: string, device: any): Promise { + async releaseDevice(id: string, nativeId: string): Promise { } async createDevice(settings: DeviceCreatorSettings): Promise { diff --git a/plugins/core/ui/package-lock.json b/plugins/core/ui/package-lock.json index 732b7952e..74df8a441 100644 --- a/plugins/core/ui/package-lock.json +++ b/plugins/core/ui/package-lock.json @@ -137,21 +137,22 @@ }, "../../../sdk": { "name": "@scrypted/sdk", - "version": "0.2.39", + "version": "0.2.55", "license": "ISC", "dependencies": { - "@babel/preset-typescript": "^7.16.7", + "@babel/preset-typescript": "^7.18.6", "adm-zip": "^0.4.13", "axios": "^0.21.4", - "babel-loader": "^8.2.3", + "babel-loader": "^9.1.0", "babel-plugin-const-enum": "^1.1.0", "esbuild": "^0.15.9", "ncp": "^2.0.0", "raw-loader": "^4.0.2", "rimraf": "^3.0.2", "tmp": "^0.2.1", - "typescript": "^4.9.3", - "webpack": "^5.74.0", + "ts-loader": "^9.4.2", + "typescript": "^4.9.4", + "webpack": "^5.75.0", "webpack-bundle-analyzer": "^4.5.0" }, "bin": { @@ -164,7 +165,7 @@ "scrypted-webpack": "bin/scrypted-webpack.js" }, "devDependencies": { - "@types/node": "^18.11.9", + "@types/node": "^18.11.18", "@types/stringify-object": "^4.0.0", "stringify-object": "^3.3.0", "ts-node": "^10.4.0", @@ -173,7 +174,7 @@ }, "../../../sdk/types": { "name": "@scrypted/types", - "version": "0.2.36", + "version": "0.2.52", "license": "ISC", "devDependencies": { "@types/rimraf": "^3.0.2", @@ -1967,7 +1968,7 @@ }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-common-types/-/6.2.1/fontawesome-common-types-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/6.2.1/fontawesome-common-types-6.2.1.tgz", "integrity": "sha512-Sz07mnQrTekFWLz5BMjOzHl/+NooTdW8F8kDQxjWwbpOJcnoSg4vUDng8d/WR1wOxM0O+CY9Zw0nR054riNYtQ==", "engines": { "node": ">=6" @@ -1975,7 +1976,7 @@ }, "node_modules/@fortawesome/fontawesome-free": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-free/-/6.2.1/fontawesome-free-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/6.2.1/fontawesome-free-6.2.1.tgz", "integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A==", "engines": { "node": ">=6" @@ -1983,7 +1984,7 @@ }, "node_modules/@fortawesome/fontawesome-svg-core": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-svg-core/-/6.2.1/fontawesome-svg-core-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/6.2.1/fontawesome-svg-core-6.2.1.tgz", "integrity": "sha512-HELwwbCz6C1XEcjzyT1Jugmz2NNklMrSPjZOWMlc+ZsHIVk+XOvOXLGGQtFBwSyqfJDNgRq4xBCwWOaZ/d9DEA==", "dependencies": { "@fortawesome/fontawesome-common-types": "6.2.1" @@ -1994,7 +1995,7 @@ }, "node_modules/@fortawesome/free-brands-svg-icons": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/free-brands-svg-icons/-/6.2.1/free-brands-svg-icons-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/6.2.1/free-brands-svg-icons-6.2.1.tgz", "integrity": "sha512-L8l4MfdHPmZlJ72PvzdfwOwbwcCAL0vx48tJRnI6u1PJXh+j2f3yDoKyQgO3qjEsgD5Fr2tQV/cPP8F/k6aUig==", "dependencies": { "@fortawesome/fontawesome-common-types": "6.2.1" @@ -2005,7 +2006,7 @@ }, "node_modules/@fortawesome/free-regular-svg-icons": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/free-regular-svg-icons/-/6.2.1/free-regular-svg-icons-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/6.2.1/free-regular-svg-icons-6.2.1.tgz", "integrity": "sha512-wiqcNDNom75x+pe88FclpKz7aOSqS2lOivZeicMV5KRwOAeypxEYWAK/0v+7r+LrEY30+qzh8r2XDaEHvoLsMA==", "dependencies": { "@fortawesome/fontawesome-common-types": "6.2.1" @@ -2016,7 +2017,7 @@ }, "node_modules/@fortawesome/free-solid-svg-icons": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/free-solid-svg-icons/-/6.2.1/free-solid-svg-icons-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/6.2.1/free-solid-svg-icons-6.2.1.tgz", "integrity": "sha512-oKuqrP5jbfEPJWTij4sM+/RvgX+RMFwx3QZCZcK9PrBDgxC35zuc7AOFsyMjMd/PIFPeB2JxyqDr5zs/DZFPPw==", "dependencies": { "@fortawesome/fontawesome-common-types": "6.2.1" @@ -22535,17 +22536,17 @@ }, "@fortawesome/fontawesome-common-types": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-common-types/-/6.2.1/fontawesome-common-types-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/6.2.1/fontawesome-common-types-6.2.1.tgz", "integrity": "sha512-Sz07mnQrTekFWLz5BMjOzHl/+NooTdW8F8kDQxjWwbpOJcnoSg4vUDng8d/WR1wOxM0O+CY9Zw0nR054riNYtQ==" }, "@fortawesome/fontawesome-free": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-free/-/6.2.1/fontawesome-free-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/6.2.1/fontawesome-free-6.2.1.tgz", "integrity": "sha512-viouXhegu/TjkvYQoiRZK3aax69dGXxgEjpvZW81wIJdxm5Fnvp3VVIP4VHKqX4SvFw6qpmkILkD4RJWAdrt7A==" }, "@fortawesome/fontawesome-svg-core": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-svg-core/-/6.2.1/fontawesome-svg-core-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/6.2.1/fontawesome-svg-core-6.2.1.tgz", "integrity": "sha512-HELwwbCz6C1XEcjzyT1Jugmz2NNklMrSPjZOWMlc+ZsHIVk+XOvOXLGGQtFBwSyqfJDNgRq4xBCwWOaZ/d9DEA==", "requires": { "@fortawesome/fontawesome-common-types": "6.2.1" @@ -22553,7 +22554,7 @@ }, "@fortawesome/free-brands-svg-icons": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/free-brands-svg-icons/-/6.2.1/free-brands-svg-icons-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/6.2.1/free-brands-svg-icons-6.2.1.tgz", "integrity": "sha512-L8l4MfdHPmZlJ72PvzdfwOwbwcCAL0vx48tJRnI6u1PJXh+j2f3yDoKyQgO3qjEsgD5Fr2tQV/cPP8F/k6aUig==", "requires": { "@fortawesome/fontawesome-common-types": "6.2.1" @@ -22561,7 +22562,7 @@ }, "@fortawesome/free-regular-svg-icons": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/free-regular-svg-icons/-/6.2.1/free-regular-svg-icons-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/6.2.1/free-regular-svg-icons-6.2.1.tgz", "integrity": "sha512-wiqcNDNom75x+pe88FclpKz7aOSqS2lOivZeicMV5KRwOAeypxEYWAK/0v+7r+LrEY30+qzh8r2XDaEHvoLsMA==", "requires": { "@fortawesome/fontawesome-common-types": "6.2.1" @@ -22569,7 +22570,7 @@ }, "@fortawesome/free-solid-svg-icons": { "version": "6.2.1", - "resolved": "https://npm.fontawesome.com/@fortawesome/free-solid-svg-icons/-/6.2.1/free-solid-svg-icons-6.2.1.tgz", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/6.2.1/free-solid-svg-icons-6.2.1.tgz", "integrity": "sha512-oKuqrP5jbfEPJWTij4sM+/RvgX+RMFwx3QZCZcK9PrBDgxC35zuc7AOFsyMjMd/PIFPeB2JxyqDr5zs/DZFPPw==", "requires": { "@fortawesome/fontawesome-common-types": "6.2.1" @@ -22800,12 +22801,12 @@ "@scrypted/sdk": { "version": "file:../../../sdk", "requires": { - "@babel/preset-typescript": "^7.16.7", - "@types/node": "^18.11.9", + "@babel/preset-typescript": "^7.18.6", + "@types/node": "^18.11.18", "@types/stringify-object": "^4.0.0", "adm-zip": "^0.4.13", "axios": "^0.21.4", - "babel-loader": "^8.2.3", + "babel-loader": "^9.1.0", "babel-plugin-const-enum": "^1.1.0", "esbuild": "^0.15.9", "ncp": "^2.0.0", @@ -22813,10 +22814,11 @@ "rimraf": "^3.0.2", "stringify-object": "^3.3.0", "tmp": "^0.2.1", + "ts-loader": "^9.4.2", "ts-node": "^10.4.0", "typedoc": "^0.23.21", - "typescript": "^4.9.3", - "webpack": "^5.74.0", + "typescript": "^4.9.4", + "webpack": "^5.75.0", "webpack-bundle-analyzer": "^4.5.0" } }, diff --git a/plugins/sip/src/main.ts b/plugins/sip/src/main.ts index 1ab32a986..703b02144 100644 --- a/plugins/sip/src/main.ts +++ b/plugins/sip/src/main.ts @@ -437,7 +437,7 @@ export class SipCamProvider extends ScryptedDeviceBase implements DeviceProvider } } - async releaseDevice(id: string, nativeId: string, device: any): Promise { + async releaseDevice(id: string, nativeId: string): Promise { } async createDevice(settings: DeviceCreatorSettings): Promise { diff --git a/plugins/sip/src/sip-call.ts b/plugins/sip/src/sip-call.ts index 3002b71a2..155525dcc 100644 --- a/plugins/sip/src/sip-call.ts +++ b/plugins/sip/src/sip-call.ts @@ -3,6 +3,7 @@ import { randomInteger, randomString } from './util' import { RtpDescription, RtpOptions, RtpStreamDescription } from './rtp-utils' import { decodeSrtpOptions } from '@homebridge/camera-utils' import { stringify } from 'sip/sip' +import { timeoutPromise } from '@scrypted/common/src/promise-utils'; const sip = require('sip'), sdp = require('sdp') @@ -15,7 +16,7 @@ export interface SipOptions { localIp: string localPort: number debugSip?: boolean - messageHandler?: SipMessageHandler + messageHandler?: SipMessageHandler shouldRegister?: boolean } @@ -178,10 +179,14 @@ export class SipCall { ws: false, logger: { recv: function(m, remote) { + if( m.status == '200' && m.reason =='Ok' && m.headers.contact ) { + // ACK for INVITE and BYE must use the registrar contact uri + this.registrarContact = m.headers.contact[0].uri; + } if( sipOptions.debugSip ) { console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") console.log(stringify( m )); - console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") + console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") } }, send: function(m, remote) { @@ -195,30 +200,31 @@ export class SipCall { // While underlying UDP socket is bound to the IP, the header is rewritten to match the domain let toWithDomain: string = (sipOptions.to.split('@')[0] + '@' + sipOptions.domain).trim() let fromWithDomain: string = (sipOptions.from.split('@')[0] + '@' + sipOptions.domain).trim() - if( m.method == 'REGISTER' || m.method == 'INVITE' ) { + if( m.method == 'REGISTER' ) { m.uri = "sip:" + sipOptions.domain } else if( m.method == 'INVITE' ) { m.uri = toWithDomain + } else if( m.method == 'ACK' || m.method == 'BYE' ) { + m.uri = this.registrarContact } else { throw new Error("Error: Method construct for uri not implemented: " + m.method) } - + m.headers.to.uri = toWithDomain m.headers.from.uri = fromWithDomain - if( m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { + if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); } - } - } + if( sipOptions.debugSip ) { console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); console.log(stringify( m )); - console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); } - }, - }, + }, + }, }, (request: SipRequest) => { if (request.method === 'BYE') { @@ -368,24 +374,27 @@ export class SipCall { /** * Register the user agent with a Registrar - */ + */ async register() { - const { from } = this.sipOptions, - inviteResponse = await this.request({ - method: 'REGISTER', - headers: { - //supported: 'replaces, outbound', - allow: - 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', - 'content-type': 'application/sdp', - contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], - }, - }); + const { from } = this.sipOptions; + await timeoutPromise( 500, + this.request({ + method: 'REGISTER', + headers: { + //supported: 'replaces, outbound', + allow: + 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', + 'content-type': 'application/sdp', + contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], + }, + }).catch(() => { + // Don't care if we get an exception here. + })); } /** * Send a message to the current call contact - */ + */ async message( content: string ) { const { from } = this.sipOptions, inviteResponse = await this.request({ @@ -399,13 +408,13 @@ export class SipCall { }, content: content }); - } + } async sendBye() { this.console.log('Sending BYE...') - return this.request({ method: 'BYE' }).catch(() => { + return await timeoutPromise( 3000, this.request({ method: 'BYE' }).catch(() => { // Don't care if we get an exception here. - }) + })); } destroy() { diff --git a/plugins/sip/src/sip-session.ts b/plugins/sip/src/sip-session.ts index 9f7c11018..a2a8cb43a 100644 --- a/plugins/sip/src/sip-session.ts +++ b/plugins/sip/src/sip-session.ts @@ -149,7 +149,6 @@ export class SipSession extends Subscribed { return rtpDescription } catch (e) { - this.callEnded(true) throw e } From 97c83c696622620bbc65efe2427b542c7bec9a7c Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 12 Jan 2023 09:07:54 -0800 Subject: [PATCH 02/21] webrtc: fix potential werift bug where srflx or relay candidate are used erroneously --- plugins/webrtc/package-lock.json | 4 ++-- plugins/webrtc/package.json | 2 +- plugins/webrtc/src/main.ts | 9 ++------- plugins/webrtc/src/werift-signaling-session.ts | 9 +++++++++ 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/plugins/webrtc/package-lock.json b/plugins/webrtc/package-lock.json index c63571620..f58627f57 100644 --- a/plugins/webrtc/package-lock.json +++ b/plugins/webrtc/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/webrtc", - "version": "0.1.18", + "version": "0.1.19", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/webrtc", - "version": "0.1.18", + "version": "0.1.19", "dependencies": { "@scrypted/common": "file:../../common", "@scrypted/sdk": "file:../../sdk", diff --git a/plugins/webrtc/package.json b/plugins/webrtc/package.json index d95aa3839..19c58307c 100644 --- a/plugins/webrtc/package.json +++ b/plugins/webrtc/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/webrtc", - "version": "0.1.18", + "version": "0.1.19", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", diff --git a/plugins/webrtc/src/main.ts b/plugins/webrtc/src/main.ts index 3d059b162..b591a5dd6 100644 --- a/plugins/webrtc/src/main.ts +++ b/plugins/webrtc/src/main.ts @@ -509,7 +509,7 @@ export class WebRTCPlugin extends AutoenableMixinProvider implements DeviceCreat export async function fork() { return { - async createConnection(message: any, portOrDummy: number | boolean, clientSession: RTCSignalingSession, maximumCompatibilityMode: boolean, transcodeWidth: number, sessionSupportsH264High: boolean, options: { disableIntercom?: boolean; configuration: RTCConfiguration, weriftConfiguration: PeerConfig; }) { + async createConnection(message: any, port: number, clientSession: RTCSignalingSession, maximumCompatibilityMode: boolean, transcodeWidth: number, sessionSupportsH264High: boolean, options: { disableIntercom?: boolean; configuration: RTCConfiguration, weriftConfiguration: PeerConfig; }) { const cleanup = new Deferred(); cleanup.promise.catch(e => this.console.log('cleaning up rtc connection:', e.message)); cleanup.promise.finally(() => setTimeout(() => process.exit(), 10000)); @@ -529,8 +529,7 @@ export async function fork() { } } - if (typeof portOrDummy === 'number') { - const port = portOrDummy; + if (port) { const socket = net.connect(port, '127.0.0.1'); cleanup.promise.finally(() => socket.destroy()); @@ -549,10 +548,6 @@ export async function fork() { } else { pc.createDataChannel('dummy'); - if (portOrDummy) { - const offer = await pc.createOffer(); - pc.setLocalDescription(offer); - } } return connection; diff --git a/plugins/webrtc/src/werift-signaling-session.ts b/plugins/webrtc/src/werift-signaling-session.ts index 48469376b..2270a79c4 100644 --- a/plugins/webrtc/src/werift-signaling-session.ts +++ b/plugins/webrtc/src/werift-signaling-session.ts @@ -1,6 +1,7 @@ import { RTCIceCandidate, RTCPeerConnection } from "./werift"; import { RTCAVSignalingSetup, RTCSignalingOptions, RTCSignalingSendIceCandidate, RTCSignalingSession } from '@scrypted/sdk'; import { createRawResponse } from "./werift-util"; +import { sleep } from "@scrypted/common/src/sleep"; export class WeriftSignalingSession implements RTCSignalingSession { remoteDescription: Promise; @@ -52,6 +53,14 @@ export class WeriftSignalingSession implements RTCSignalingSession { } async addIceCandidate(candidate: RTCIceCandidateInit) { + // todo: fix this in werift or verify it still occurs at later point + // werift seems to choose whatever candidate pair results in the fastest connection. + // this makes it sometimes choose the STUN or TURN candidate even when + // on the local network. + if (candidate.candidate?.includes('relay')) + await sleep(500); + else if (candidate.candidate?.includes('srflx')) + await sleep(250); await this.pc.addIceCandidate(new RTCIceCandidate(candidate)); } } From 36f0f2b1f265bec808d81878d9bb14c9680f364e Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 12 Jan 2023 09:31:52 -0800 Subject: [PATCH 03/21] core: support readonly device picker --- plugins/core/ui/src/common/DevicePicker.vue | 6 +++--- plugins/core/ui/src/interfaces/Setting.vue | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/core/ui/src/common/DevicePicker.vue b/plugins/core/ui/src/common/DevicePicker.vue index feb41243a..0e1656764 100644 --- a/plugins/core/ui/src/common/DevicePicker.vue +++ b/plugins/core/ui/src/common/DevicePicker.vue @@ -1,6 +1,6 @@