From 49ec71e3f85f9e235cd72c21251e2d16f4cef06d Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Mon, 14 Mar 2022 16:51:34 -0700 Subject: [PATCH] rebroadcast: scrypted rtsp parser needs timeout. --- common/src/rtsp-server.ts | 4 +- plugins/prebuffer-mixin/package-lock.json | 4 +- plugins/prebuffer-mixin/package.json | 2 +- plugins/prebuffer-mixin/src/main.ts | 47 +++++++++++++---------- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/common/src/rtsp-server.ts b/common/src/rtsp-server.ts index e5ace0f74..6ab835b58 100644 --- a/common/src/rtsp-server.ts +++ b/common/src/rtsp-server.ts @@ -8,6 +8,7 @@ import net from 'net'; import tls from 'tls'; import { DIGEST } from 'http-auth-utils/dist/index'; import crypto from 'crypto'; +import { timeoutPromise } from './promise-utils'; export const RTSP_FRAME_MAGIC = 36; @@ -138,6 +139,7 @@ export class RtspClient extends RtspBase { cseq = 0; session: string; authorization: string; + requestTimeout: number; constructor(public url: string, console?: Console) { super(console); @@ -190,7 +192,7 @@ export class RtspClient extends RtspBase { }> { this.writeRequest(method, headers, path, body); - const message = await this.readMessage(); + const message = this.requestTimeout ? await timeoutPromise(this.requestTimeout, this.readMessage()) : await this.readMessage(); const status = message[0]; const response = parseHeaders(message); if (!status.includes('200') && !response['www-authenticate']) diff --git a/plugins/prebuffer-mixin/package-lock.json b/plugins/prebuffer-mixin/package-lock.json index 31b450883..5af91cbde 100644 --- a/plugins/prebuffer-mixin/package-lock.json +++ b/plugins/prebuffer-mixin/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/prebuffer-mixin", - "version": "0.1.200", + "version": "0.1.201", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/prebuffer-mixin", - "version": "0.1.200", + "version": "0.1.201", "license": "Apache-2.0", "dependencies": { "@scrypted/common": "file:../../common", diff --git a/plugins/prebuffer-mixin/package.json b/plugins/prebuffer-mixin/package.json index c03285e27..8cb5ccef0 100644 --- a/plugins/prebuffer-mixin/package.json +++ b/plugins/prebuffer-mixin/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/prebuffer-mixin", - "version": "0.1.200", + "version": "0.1.201", "description": "Rebroadcast and Prebuffer for VideoCameras.", "author": "Scrypted", "license": "Apache-2.0", diff --git a/plugins/prebuffer-mixin/src/main.ts b/plugins/prebuffer-mixin/src/main.ts index 73fbf9977..585298f64 100644 --- a/plugins/prebuffer-mixin/src/main.ts +++ b/plugins/prebuffer-mixin/src/main.ts @@ -570,28 +570,35 @@ class PrebufferSession { const parser = this.getParser(rtspMode, muxingMp4, ffmpegInput.mediaStreamOptions); if (parser === SCRYPTED_PARSER) { usingScryptedParser = true; - this.console.log('bypassing ffmpeg: using scrypted rtsp/rfc4571 parser') + this.console.log('bypassing ffmpeg: using scrypted rtsp/rfc4571 parser'); const rtspClient = new RtspClient(ffmpegInput.url, this.console); - await rtspClient.options(); - const sdpResponse = await rtspClient.describe(); - const sdp = sdpResponse.body.toString().trim(); - this.console.log('sdp', sdp); - this.sdp = Promise.resolve(sdp); - const { audio, video } = parseTrackIds(sdp); - let channel = 0; - if (!audioSoftMuted) { - await rtspClient.setup(channel, audio); - channel += 2; + try { + rtspClient.requestTimeout = 10000; + await rtspClient.options(); + const sdpResponse = await rtspClient.describe(); + const sdp = sdpResponse.body.toString().trim(); + this.console.log('sdp', sdp); + this.sdp = Promise.resolve(sdp); + const { audio, video } = parseTrackIds(sdp); + let channel = 0; + if (!audioSoftMuted) { + await rtspClient.setup(channel, audio); + channel += 2; + } + await rtspClient.setup(channel, video); + const socket = await rtspClient.play(); + session = await startRFC4571Parser(this.console, socket, sdp, ffmpegInput.mediaStreamOptions, true, rbo); + const sessionKill = session.kill.bind(session); + session.kill = async () => { + // issue a teardown to upstream to close gracefully but don't rely on it responding. + rtspClient.teardown().finally(sessionKill); + await sleep(500); + sessionKill(); + } } - await rtspClient.setup(channel, video); - const socket = await rtspClient.play(); - session = await startRFC4571Parser(this.console, socket, sdp, ffmpegInput.mediaStreamOptions, true, rbo); - const sessionKill = session.kill.bind(session); - session.kill = async () => { - // issue a teardown to upstream to close gracefully but don't rely on it responding. - rtspClient.teardown().finally(sessionKill); - await sleep(500); - sessionKill(); + catch (e) { + rtspClient.client.destroy(); + throw e; } } else {