From f84f5349df66f3d58fabdebd0240b41dff234d3d Mon Sep 17 00:00:00 2001 From: Roger Hardiman Date: Fri, 23 Jan 2026 08:29:59 +0000 Subject: [PATCH 1/3] Add SMPTE336M and STANAG 4609 KLV support. Write to a raw .klv file. Tested with a MPEGTS stream with KLV data fed into a MediaMTX 1.15.6 and then out with Yellowstone RTSP. The raw .klv file can be parsed with Python's klvdata parser --- CHANGELOG.md | 1 + README.md | 1 + examples/demo.js | 8 +++- lib/RTSPClient.ts | 13 +++++++ lib/index.ts | 2 + lib/transports/SMPTE336MKLVTransport.ts | 52 +++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 lib/transports/SMPTE336MKLVTransport.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ba146b..3a945fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # Next Release +Add SMPTE336M and STANAG 4609 KLV parsing and writing to a raw .klv data file. Testing by passing MPEGTS with KLV data into a recent version of MediaMTX that outputs KLV over RTSP # 3.0.8 - 15th January 2027 Add caching Digest Authentication code from Leone25 Enrico diff --git a/README.md b/README.md index 580e45d..8782dfc 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Yellowstone is co-developed by Michael Bullington and Roger Hardiman. - ONVIF Metadata parsing (and writing to an output file) - ONVIF extensions to RTSP - ONVIF Audio Backchannel, sending ALaw audio to an IP Camera +- SMPTE336M KLV (STANAG 4609) KLV parsing (and writing to a .klv file) - Simple RTCP parsing ## Examples diff --git a/examples/demo.js b/examples/demo.js index 74a9de8..84e77ee 100644 --- a/examples/demo.js +++ b/examples/demo.js @@ -13,7 +13,7 @@ // Used to connect to Wowza Demo URL but they have taken it away, and the replacement URL on their web site does not work. -const { RTSPClient, H264Transport, H265Transport, H266Transport, AV1Transport, AACTransport } = require("../dist"); +const { RTSPClient, H264Transport, H265Transport, H266Transport, AV1Transport, AACTransport, SMPTE336MKLVTransport } = require("../dist"); const fs = require("fs"); const { exit } = require("process"); const { program } = require("commander"); @@ -96,6 +96,12 @@ client.connect(url, { connection: transport, secure: false }) // This class subscribes to the client 'data' event, looking for the audio payload const aac = new AACTransport(client, audioFile, details); } + if (details.codec == "SMPTE336M") { + const klvFile = fs.createWriteStream(filename + '.klv'); + // Add KLV Transport + // This class subscribes to the client 'data' event, looking for the KLV payload + const klv = new SMPTE336MKLVTransport(client, klvFile, details); + } } // Step 5: Start streaming! diff --git a/lib/RTSPClient.ts b/lib/RTSPClient.ts index 5590a39..928f188 100644 --- a/lib/RTSPClient.ts +++ b/lib/RTSPClient.ts @@ -387,6 +387,19 @@ export default class RTSPClient extends EventEmitter { } } + if ( + mediaSource.type === "application" && + mediaSource.protocol === RTP_AVP && + mediaSource.rtp[0].codec.toUpperCase() === "SMPTE336M" // MediaMTX sends in capitals. Looks like the RFC suggests lower case + ) { + this.emit("log", "SMPTE336M KLV Data Stream Found in SDP", ""); + if (hasMetaData == false) { + needSetup = true; + hasMetaData = true; + codec = "SMPTE336M"; + } + } + if (needSetup) { let streamurl = ""; // The 'control' in the SDP can be a relative or absolute uri diff --git a/lib/index.ts b/lib/index.ts index f263eaa..0c14000 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -3,6 +3,7 @@ import H265Transport from "./transports/H265Transport"; import H266Transport from "./transports/H266Transport"; import AV1Transport from "./transports/AV1Transport"; import AACTransport from "./transports/AACTransport"; +import SMPTE336MKLVTransport from "./transports/SMPTE336MKLVTransport"; import ONVIFMetadataTransport from "./transports/ONVIFMetadataTransport"; import ONVIFClient from "./ONVIFClient"; import RTSPClient from "./RTSPClient"; @@ -14,6 +15,7 @@ export { H266Transport, AV1Transport, AACTransport, + SMPTE336MKLVTransport, ONVIFMetadataTransport, ONVIFClient, RTSPClient, diff --git a/lib/transports/SMPTE336MKLVTransport.ts b/lib/transports/SMPTE336MKLVTransport.ts new file mode 100644 index 0000000..dc5a6ad --- /dev/null +++ b/lib/transports/SMPTE336MKLVTransport.ts @@ -0,0 +1,52 @@ +// De-packetize RC 6597 RTP packets to re-create SMPTE336M KLV Metadata including STANAG 4609 +// Write data to a file as raw binary data. +// The RTP timestamp is not saved to the file. +// By Roger Hardiman, January 2026 + +import RTSPClient from "../RTSPClient"; +import { RTPPacket } from "../util"; + +import * as transform from "sdp-transform"; +import { Writable } from "stream"; + +interface Details { + codec: string; + mediaSource: transform.MediaDescription; + rtpChannel: number; + rtcpChannel: number; +} + +export default class SMPTE336MKLVTransport { + client: RTSPClient; + stream: Writable; + rawData: Buffer[]; + + constructor(client: RTSPClient, stream: Writable, details: Details) { + this.client = client; + this.stream = stream; + this.rawData = []; + + client.on("data", (channel, data, packet) => { + if (channel == details.rtpChannel) { + this.processRTPPacket(packet); + } + }); + } + + processRTPPacket(packet: RTPPacket): void { + // RTP Payload for ONVIF Metadata + + // Accumulate payload + this.rawData.push(packet.payload) + + if (packet.marker == 1) { // TODO... OR if the Timestamp has changed + // end of data. Write the file + // In this case we can just write each Buffer from the rawData array + // If we were passing the KLV to a caller, we would concatenate the Buffers in the rawData array first + for(const buffer of this.rawData) { + this.stream.write(buffer); + } + this.rawData = []; + } + } +} From a26e39059fab2cc6705bea07f6e8bffaffc4f03a Mon Sep 17 00:00:00 2001 From: Roger Hardiman Date: Fri, 23 Jan 2026 08:32:05 +0000 Subject: [PATCH 2/3] Handle H264 streams with no SPS and PPS (sprop-parameter-sets) in the SDP. This is the case when MediaMTX (1.15.6) converts a MPEGTS stream into a RTSP Server Was also noted that MediaMTX used Payload 96 for both the video stream, and the audio stream and the KLV metadata stream. --- lib/transports/H264Transport.ts | 40 +++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/lib/transports/H264Transport.ts b/lib/transports/H264Transport.ts index 46270f3..682826c 100644 --- a/lib/transports/H264Transport.ts +++ b/lib/transports/H264Transport.ts @@ -49,19 +49,35 @@ export default class H264Transport { return; } + // Normally the SDP includes the sprop-parameter-sets with the SPS and PPS data + // However for MPEGTS converted to RTSP with MediaMTX, there is no sprop-parameter-sets const fmtpConfig = transform.parseParams(fmtp.config); - const splitSpropParameterSets = fmtpConfig['sprop-parameter-sets'].toString().split(','); - const sps_base64 = splitSpropParameterSets[0]; - const pps_base64 = splitSpropParameterSets[1]; - const sps = Buffer.from(sps_base64, "base64"); - const pps = Buffer.from(pps_base64, "base64"); - - this.stream.write(H264_HEADER); - this.stream.write(sps); - this.stream.write(H264_HEADER); - this.stream.write(pps); - - this._headerWritten = true; + if ('sprop-parameter-sets' in fmtpConfig) { + const splitSpropParameterSets = fmtpConfig['sprop-parameter-sets'].toString().split(','); + const sps_base64 = splitSpropParameterSets[0]; + const pps_base64 = splitSpropParameterSets[1]; + const sps = Buffer.from(sps_base64, "base64"); + const pps = Buffer.from(pps_base64, "base64"); + + this.stream.write(H264_HEADER); + this.stream.write(sps); + this.stream.write(H264_HEADER); + this.stream.write(pps); + + this._headerWritten = true; + } + else + { + // Ideally MediaMTX would have parsed the MPEGTS stream, extracted the SPS and PPS and then + // placed it in the RTSP DESCRIBE SDP, but it does not do that. + // The correct method is to parse the RTP Payloads until we see the NAL type for SPS + // and the NAL type for PPS, then we can write them and set this._headerWritten to true + + // But for now we will just set this._headerWritten to true and let NALS be written to disk + // before the first SPS and PPS data + this._headerWritten = true; // HACK - should be parsing the NALs for SPS and PPS + } + } processRTPPacket(packet: RTPPacket): void { From 06f8df7827114af17a26032c122a815fbb3d2cf1 Mon Sep 17 00:00:00 2001 From: Roger Hardiman Date: Fri, 23 Jan 2026 08:33:59 +0000 Subject: [PATCH 3/3] Update dist files Really must look at the automated methods suggested in a PR --- dist/RTSPClient.js | 11 +++++++ dist/RTSPClient.js.map | 2 +- dist/index.d.ts | 3 +- dist/index.js | 4 ++- dist/index.js.map | 2 +- dist/transports/H264Transport.js | 33 +++++++++++++------ dist/transports/H264Transport.js.map | 2 +- dist/transports/SMPTE336MKLVTransport.d.ts | 20 ++++++++++++ dist/transports/SMPTE336MKLVTransport.js | 34 ++++++++++++++++++++ dist/transports/SMPTE336MKLVTransport.js.map | 1 + 10 files changed, 97 insertions(+), 15 deletions(-) create mode 100644 dist/transports/SMPTE336MKLVTransport.d.ts create mode 100644 dist/transports/SMPTE336MKLVTransport.js create mode 100644 dist/transports/SMPTE336MKLVTransport.js.map diff --git a/dist/RTSPClient.js b/dist/RTSPClient.js index f3ace84..eb76f5c 100644 --- a/dist/RTSPClient.js +++ b/dist/RTSPClient.js @@ -248,6 +248,17 @@ class RTSPClient extends events_1.EventEmitter { codec = "vnd.onvif.metadata"; } } + if (mediaSource.type === "application" && + mediaSource.protocol === RTP_AVP && + mediaSource.rtp[0].codec.toUpperCase() === "SMPTE336M" // MediaMTX sends in capitals. Looks like the RFC suggests lower case + ) { + this.emit("log", "SMPTE336M KLV Data Stream Found in SDP", ""); + if (hasMetaData == false) { + needSetup = true; + hasMetaData = true; + codec = "SMPTE336M"; + } + } if (needSetup) { let streamurl = ""; // The 'control' in the SDP can be a relative or absolute uri diff --git a/dist/RTSPClient.js.map b/dist/RTSPClient.js.map index c549169..749fac2 100644 --- a/dist/RTSPClient.js.map +++ b/dist/RTSPClient.js.map @@ -1 +1 @@ -{"version":3,"file":"RTSPClient.js","sourceRoot":"","sources":["../lib/RTSPClient.ts"],"names":[],"mappings":";;AAAA,2BAA2B;AAC3B,2BAA2B;AAK3B,+BAA+B;AAC/B,6BAAwC;AACxC,mCAAsC;AAItC,iCAQgB;AAEhB,2CAA2C;AAC3C,sDAA+C;AAC/C,MAAM,OAAO,GAAG,SAAS,CAAC;AAC1B,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,kFAAkF;AAE/G,MAAM,SAAS,GAAG,GAAG,CAAC;AACtB,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B,gCAAgC;AAChC,uBAAuB;AACvB,qCAAqC;AACrC,uCAAuC;AAEvC,sCAAsC;AACtC,wCAAwC;AACxC,qCAAqC;AACrC,qHAAqH;AACrH,mCAAmC;AAEnC,6BAA6B;AAC7B,QAAQ;AACR,4BAA4B;AAC5B,oDAAoD;AACpD,8BAA8B;AAC9B,4BAA4B;AAC5B,8BAA8B;AAC9B,+CAA+C;AAC/C,EAAE;AACF,gCAAgC;AAChC,4FAA4F;AAC5F,iNAAiN;AACjN,sEAAsE;AAEtE,MAAM,QAAQ,GAAG,kBAAkB,CAAC;AACpC,MAAM,cAAc,GAAG,IAAI,MAAM,CAAC,iFAAiF,EAAE,GAAG,CAAC,CAAC;AAE1H,IAAK,UAMJ;AAND,WAAK,UAAU;IACb,qDAAS,CAAA;IACT,yEAAmB,CAAA;IACnB,2EAAoB,CAAA;IACpB,iFAAuB,CAAA;IACvB,uEAAkB,CAAA;AACpB,CAAC,EANI,UAAU,KAAV,UAAU,QAMd;AAyCD,MAAqB,UAAW,SAAQ,qBAAY;IA6ClD,YACE,QAAgB,EAChB,QAAgB,EAChB,OAAmC;QAEnC,KAAK,EAAE,CAAC;QA7CV,gBAAW,GAAG,KAAK,CAAC;QACpB,WAAM,GAAG,KAAK,CAAC;QAMf,UAAK,GAAG,CAAC,CAAC;QAMV,gCAA2B,GAAG,CAAC,CAAC;QAChC,qBAAgB,GAAG,IAAI,CAAC;QAExB,cAAS,GAAe,UAAU,CAAC,SAAS,CAAC;QAE7C,uCAAuC;QACvC,iDAAiD;QACjD,iBAAY,GAAa,EAAE,CAAC;QAE5B,mCAAmC;QAEnC,6CAA6C;QAC7C,sBAAiB,GAAG,CAAC,CAAC;QACtB,mBAAc,GAAG,EAAE,CAAC;QACpB,gBAAW,GAAY,EAAE,CAAC;QAE1B,uCAAuC;QAEvC,qBAAgB,GAAG,CAAC,CAAC;QACrB,eAAU,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7B,sBAAiB,GAAG,CAAC,CAAC;QAEtB,iCAAiC;QACjC,eAAU,GAAG,IAAA,mBAAY,GAAE,CAAC;QAE5B,cAAS,GAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QAC1C,gBAAW,GAAkB,EAAE,CAAC;QAy5BhC,mBAAc,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;QAj5B9C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,OAAO,mCACP,CAAC,OAAO,IAAI,EAAE,CAAC,KAClB,YAAY,EAAE,iBAAiB,GAChC,CAAC;IACJ,CAAC;IAED,qDAAqD;IACrD,YAAY;IACZ,EAAE;IACF,iBAAiB;IACjB,EAAE;IACF,uDAAuD;IACvD,YAAY;IACZ,WAAW,CAAC,QAAgB,EAAE,IAAY,EAAE,SAAkB,KAAK;QACjE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,+BAA+B;YAE/B,MAAM,aAAa,GAAG,CAAC,GAAQ,EAAE,EAAE;gBACjC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;gBAC9C,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC;YAEF,MAAM,wBAAwB,GAAG,CAAC,GAAQ,EAAE,EAAE;gBAC5C,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;gBACzD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;gBACxB,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC;YAEF,MAAM,aAAa,GAAG,GAAG,EAAE;gBACzB,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;gBAC9C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACnB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC,CAAC;YAEF,MAAM,gBAAgB,GAAG,CAAC,YAAoB,EAAE,OAAgB,EAAE,EAAE;gBAClE,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBAExC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;oBAC/B,OAAO;iBACR;gBAED,IAAI,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,UAAU,EAAE;oBAC9C,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;iBAChD;gBAED,IAAI,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,QAAQ,EAAE;oBAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;oBACb,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;iBAChC;YACH,CAAC,CAAC;YAEF,0BAA0B;YAC1B,IAAI,MAAmB,CAAC;YACxB,IAAI,MAAM,IAAI,KAAK,EAAE;gBACnB,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE;oBACxC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;oBACxB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;oBAEtB,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;oBAC9C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;oBAE7C,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;oBACtC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC,CAAC,CAAC;aACJ;iBACI;gBACH,MAAM,OAAO,GAA0B;oBACrC,kBAAkB,EAAE,KAAK;iBAC1B,CAAC;gBACF,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE;oBACjD,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;oBAC9B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;oBACxB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;oBAEtB,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;oBAE9C,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;oBACtC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC,CAAC,CAAC;aACJ;YAED,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAC3C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;YAClC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;YAClC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CACX,GAAW,EACX,EACE,SAAS,GAAG,IAAI,EAChB,UAAU,GAAG,KAAK,EAClB,MAAM,GAAG,KAAK,MACsD;QAClE,SAAS,EAAE,IAAI;QACf,UAAU,EAAE,KAAK;QACjB,MAAM,EAAE,KAAK;KACd;QAEH,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAA,WAAQ,EAAC,CAAC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,EAAE;YACb,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;SACzD;QAED,MAAM,OAAO,GAAa,EAAE,CAAC;QAE7B,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,IAAI,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;QAClE,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAE9B,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;YACjD,MAAM,EAAE,iBAAiB;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE;YAC7C,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;SACH;QAED,sFAAsF;QACtF,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAEzE,oEAAoE;QACpE,gGAAgG;QAChG,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,IAAI,cAAc,GAAG,KAAK,CAAC;QAE3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACrC,IAAI,SAAS,GAAG,KAAK,CAAC;YACtB,IAAI,KAAK,GAAG,EAAE,CAAC;YACf,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAG7B,kHAAkH;YAClH,mCAAmC;YACnC,IAAI,WAAW,CAAC,SAAS,IAAI,SAAS;gBAAE,WAAW,CAAC,SAAS,GAAG,UAAU,CAAC,CAAC,mCAAmC;YAE/G,IACE,WAAW,CAAC,IAAI,KAAK,OAAO;gBAC5B,WAAW,CAAC,QAAQ,KAAK,OAAO;gBAChC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,EACnC;gBACA,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,gCAAgC,EAAE,EAAE,CAAC,CAAC;gBACvD,IAAI,QAAQ,IAAI,KAAK,EAAE;oBACrB,SAAS,GAAG,IAAI,CAAC;oBACjB,QAAQ,GAAG,IAAI,CAAC;oBAChB,KAAK,GAAG,MAAM,CAAC;iBAChB;aACF;YAED,IACE,WAAW,CAAC,IAAI,KAAK,OAAO;gBAC5B,WAAW,CAAC,QAAQ,KAAK,OAAO;gBAChC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,EACnC;gBACA,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,gCAAgC,EAAE,EAAE,CAAC,CAAC;gBACvD,IAAI,QAAQ,IAAI,KAAK,EAAE;oBACrB,SAAS,GAAG,IAAI,CAAC;oBACjB,QAAQ,GAAG,IAAI,CAAC;oBAChB,KAAK,GAAG,MAAM,CAAC;iBAChB;aACF;YAED,IACE,WAAW,CAAC,IAAI,KAAK,OAAO;gBAC5B,WAAW,CAAC,QAAQ,KAAK,OAAO;gBAChC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,EACnC;gBACA,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,gCAAgC,EAAE,EAAE,CAAC,CAAC;gBACvD,IAAI,QAAQ,IAAI,KAAK,EAAE;oBACrB,SAAS,GAAG,IAAI,CAAC;oBACjB,QAAQ,GAAG,IAAI,CAAC;oBAChB,KAAK,GAAG,MAAM,CAAC;iBAChB;aACF;YAED,IACE,WAAW,CAAC,IAAI,KAAK,OAAO;gBAC5B,CAAC,WAAW,CAAC,QAAQ,KAAK,OAAO,IAAI,WAAW,CAAC,QAAQ,KAAK,QAAQ,CAAC;gBACvE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,EAClC;gBACA,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,+BAA+B,EAAE,EAAE,CAAC,CAAC;gBACtD,IAAI,QAAQ,IAAI,KAAK,EAAE;oBACrB,SAAS,GAAG,IAAI,CAAC;oBACjB,QAAQ,GAAG,IAAI,CAAC;oBAChB,KAAK,GAAG,KAAK,CAAC;iBACf;aACF;YAED,IACE,WAAW,CAAC,IAAI,KAAK,OAAO;gBAC5B,CAAC,WAAW,CAAC,SAAS,KAAK,UAAU,IAAI,WAAW,CAAC,SAAS,KAAK,UAAU,CAAC;gBAC9E,WAAW,CAAC,QAAQ,KAAK,OAAO;gBAChC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,eAAe,IAAI,6DAA6D;gBAC3H,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAC1C;gBACA,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,+BAA+B,EAAE,EAAE,CAAC,CAAC;gBACtD,IAAI,QAAQ,IAAI,KAAK,EAAE;oBACrB,SAAS,GAAG,IAAI,CAAC;oBACjB,QAAQ,GAAG,IAAI,CAAC;oBAChB,KAAK,GAAG,KAAK,CAAC;iBACf;aACF;YAED,IAAI,WAAW,CAAC,IAAI,KAAK,OAAO;gBAC9B,WAAW,CAAC,SAAS,KAAK,UAAU;gBACpC,WAAW,CAAC,QAAQ,KAAK,OAAO,EAAE;gBAClC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,gCAAgC,EAAE,EAAE,CAAC,CAAC;gBACvD,IAAI,cAAc,IAAI,KAAK,EAAE;oBAC3B,SAAS,GAAG,IAAI,CAAC;oBACjB,cAAc,GAAG,IAAI,CAAC;oBACtB,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;iBAClC;aACF;YAED,IACE,WAAW,CAAC,IAAI,KAAK,aAAa;gBAClC,WAAW,CAAC,QAAQ,KAAK,OAAO;gBAChC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,oBAAoB,EAC/D;gBACA,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,8BAA8B,EAAE,EAAE,CAAC,CAAC;gBACrD,IAAI,WAAW,IAAI,KAAK,EAAE;oBACxB,SAAS,GAAG,IAAI,CAAC;oBACjB,WAAW,GAAG,IAAI,CAAC;oBACnB,KAAK,GAAG,oBAAoB,CAAC;iBAC9B;aACF;YAED,IAAI,SAAS,EAAE;gBACb,IAAI,SAAS,GAAG,EAAE,CAAC;gBACnB,6DAA6D;gBAC7D,IAAI,WAAW,CAAC,OAAO,EAAE;oBACvB,IAAI,WAAW,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;wBAC3D,gBAAgB;wBAChB,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC;qBACjC;yBAAM;wBACL,gBAAgB;wBAChB,SAAS,GAAG,IAAI,CAAC,IAAI,GAAG,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC;qBACnD;iBACF;gBAED,mCAAmC;gBACnC,gCAAgC;gBAChC,qFAAqF;gBACrF,IAAI,QAAQ,CAAC;gBACb,IAAI,UAAkB,CAAC;gBACvB,IAAI,WAAmB,CAAC;gBACxB,IAAI,WAAW,GAAsB,IAAI,CAAC,CAAC,sBAAsB;gBACjE,IAAI,YAAY,GAAsB,IAAI,CAAC,CAAC,sBAAsB;gBAElE,IAAI,UAAU,KAAK,KAAK,EAAE;oBACxB,6DAA6D;oBAC7D,iCAAiC;oBAEjC,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC;oBACnC,WAAW,GAAG,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC;oBACxC,IAAI,CAAC,gBAAgB,IAAI,CAAC,CAAC;oBAE3B,MAAM,OAAO,GAAG,UAAU,CAAC;oBAC3B,WAAW,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;oBAEzC,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;wBACxC,IAAI,MAAM,GAAG,IAAA,qBAAc,EAAC,GAAG,CAAC,CAAC;wBAEjC,sBAAsB;wBACtB,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,CAAC;wBAC5E,IAAI,MAAM,IAAI,SAAS;4BAAE,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;wBAEtF,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBACrD,CAAC,CAAC,CAAC;oBAEH,MAAM,QAAQ,GAAG,WAAW,CAAC;oBAC7B,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;oBAE1C,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;wBACzC,MAAM,MAAM,GAAG,IAAA,sBAAe,EAAC,GAAG,CAAC,CAAC;wBAEpC,4DAA4D;wBAC5D,IAAI,MAAM,CAAC,UAAU,IAAI,GAAG,IAAI,MAAM,CAAC,YAAY,IAAI,SAAS,EAAE;4BAChE,IAAI,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,CAAC;4BAC5E,IAAI,MAAM,IAAI,SAAS,EAAE;gCACvB,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC;gCACvD,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC;gCACvD,MAAM,CAAC,eAAe,GAAG,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC;6BAC3D;yBACF;wBAED,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;wBAE3C,MAAM,eAAe,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;wBACpD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;oBAClE,CAAC,CAAC,CAAC;oBAEH,yCAAyC;oBAEzC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;wBAC5B,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;oBAChD,CAAC,CAAC,CAAC;oBAEH,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;wBAC5B,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;oBAClD,CAAC,CAAC,CAAC;oBAEH,MAAM,WAAW,GAAG;wBAClB,SAAS,EAAE,+BAA+B,OAAO,IAAI,QAAQ,EAAE;qBAChE,CAAC;oBACF,IAAI,IAAI,CAAC,QAAQ;wBACf,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;oBACzD,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;iBAChE;qBAAM,IAAI,UAAU,KAAK,KAAK,EAAE;oBAC/B,iBAAiB;oBACjB,kBAAkB;oBAElB,UAAU,GAAG,IAAI,CAAC,2BAA2B,CAAC;oBAC9C,WAAW,GAAG,IAAI,CAAC,2BAA2B,GAAG,CAAC,CAAC;oBACnD,IAAI,CAAC,2BAA2B,IAAI,CAAC,CAAC;oBAEtC,MAAM,WAAW,GAAG;wBAClB,SAAS,EAAE,2BAA2B,UAAU,IAAI,WAAW,EAAE;qBAClE,CAAC;oBACF,IAAI,IAAI,CAAC,QAAQ;wBACf,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,0BAA0B;oBACpF,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;iBAChE;qBAAM;oBACL,MAAM,IAAI,KAAK,CACb,iDAAiD,UAAU,mBAAmB,CAC/E,CAAC;iBACH;gBAED,IAAI,CAAC,QAAQ,EAAE;oBACb,MAAM,IAAI,KAAK,CACb,yDAAyD,CAC1D,CAAC;iBACH;gBAED,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;gBAE7B,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE;oBACtB,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;iBACH;gBAED,MAAM,SAAS,GAAG,IAAA,qBAAc,EAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBACpD;gBACE,MAAM;gBACN,SAAS,CAAC,QAAQ,KAAK,aAAa;oBACpC,MAAM;oBACN,SAAS,CAAC,QAAQ,KAAK,SAAS;oBAChC,MAAM;oBACN,SAAS,CAAC,QAAQ,KAAK,aAAa,CAAC,8BAA8B;kBACnE;oBACA,MAAM,IAAI,KAAK,CACb,gGAAgG,CACjG,CAAC;iBACH;gBAED,qCAAqC;gBACrC,uEAAuE;gBACvE,mEAAmE;gBACnE,iFAAiF;gBACjF,uGAAuG;gBACvG,IAAI,UAAU,KAAK,KAAK,IAAI,SAAS,IAAI,WAAW,IAAI,YAAY,EAAE;oBACpE,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;oBACvG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;iBACzG;gBAED,IAAI,OAAO,CAAC,WAAW,EAAE;oBACvB,IAAI,CAAC,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;iBAC9D;gBAED,IAAI,OAAO,CAAC,OAAO,EAAE;oBACnB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;iBAC/C;gBAED,MAAM,MAAM,GAAW;oBACrB,KAAK;oBACL,WAAW;oBACX,SAAS,EAAE,SAAS,CAAC,UAAU;oBAC/B,MAAM,EAAE,KAAK,KAAK,MAAM;oBACxB,UAAU;oBACV,WAAW;iBACZ,CAAC;gBAEF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;aACtB,CAAC,qBAAqB;SACxB,CAAC,+CAA+C;QAEjD,IAAI,SAAS,EAAE;YACb,sEAAsE;YACtE,uBAAuB;YACvB,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;gBACnC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACpD,kCAAkC;YACpC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;SACf;QAED,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;QAC3B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,CACL,WAAmB,EACnB,eAAwB,EAAE,EAC1B,GAAY;QAEZ,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YACjB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;SAC1B;QAED,IAAI,CAAC,GAAG,EAAE;YACR,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC;SACjB;QAED,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC;QACxB,8BAA8B;QAC9B,IAAI,GAAG,GAAG,GAAG,WAAW,IAAI,GAAG,sBAAsB,EAAE,MAAM,CAAC;QAE9D,MAAM,OAAO,mCACR,IAAI,CAAC,OAAO,GACZ,YAAY,CAChB,CAAC;QAEF,IAAI,IAAI,CAAC,WAAW,EAAE;YACpB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;gBACrB,aAAa,EAAE,IAAI,CAAC,mBAAmB,CAAC,WAAW,EAAE,GAAG,CAAC;aAC1D,CAAC,CAAC;SACJ;QAED,QAAQ;QACR,sEAAsE;QACtE,0EAA0E;QAC1E,iFAAiF;QAEjF,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;aAC3B,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,KAAK,MAAM,CAAC;aAC7C,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAC9B,oDAAoD;QACpD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;QAEjC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,eAAe,GAAG,CACtB,YAAoB,EACpB,UAAmB,EACnB,YAAsB,EACtB,EAAE;gBACF,MAAM,WAAW,GAAW,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBACzD,IAAI,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,eAAe,IAAI,OAAO,EAAE;oBACjE,6FAA6F;oBAC7F,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;oBAC3C,OAAO;iBACR;gBACD,IAAI,UAAU,CAAC,IAAI,KAAK,EAAE,EAAE;oBAC1B,OAAO;iBACR;gBAED,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;gBAEjD,MAAM,UAAU,GAAG,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAExD,IAAI,UAAU,KAAK,SAAS,EAAE;oBAC5B,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE;wBAC3B,OAAO,CAAC;4BACN,OAAO,EAAE,UAAU;4BACnB,YAAY;yBACb,CAAC,CAAC;qBACJ;yBAAM;wBACL,OAAO,CAAC;4BACN,OAAO,EAAE,UAAU;yBACpB,CAAC,CAAC;qBACJ;iBACF;qBAAM;oBACL,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;oBAExC,uCAAuC;oBACvC,IAAI,UAAU,KAAK,aAAa,IAAI,UAAU,EAAE;wBAC9C,IAAI,CAAC,WAAW,GAAG;4BACjB,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAwB;4BACrD,SAAS,EAAE,KAAK,EAAE,iGAAiG;yBACpH,CAAA;wBAED,4CAA4C;wBAC5C,IAAI,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;wBAC5C,OAAO,KAAK,IAAI,IAAI,EAAE;4BACpB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;4BAEtB,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE;gCAC/B,IAAI,CAAC,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;6BACnC;4BAED,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE;gCAC/B,IAAI,CAAC,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;6BACnC;4BAED,IAAI,IAAI,IAAI,WAAW,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE;gCACnC,IAAI,CAAC,WAAW,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAA6B,CAAC;6BACnE;4BAED,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;yBACzC;wBAED,uGAAuG;wBACvG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;wBACjD,OAAO;qBACR;oBAED,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,UAAU,GAAG,CAAC,CAAC,CAAC;oBACzD,OAAO;iBACR;YACH,CAAC,CAAC;YAEF,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,MAAc,EAAE,eAAwB,EAAE;QAChD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YACjB,OAAO;SACR;QAED,8BAA8B;QAC9B,IAAI,GAAG,GAAG,YAAY,MAAM,MAAM,CAAC;QAEnC,MAAM,OAAO,mCACR,IAAI,CAAC,OAAO,GACZ,YAAY,CAChB,CAAC;QAEF,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;aAC3B,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,KAAK,MAAM,CAAC;aAC7C,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAC9B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YACrB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;SAC7C;QAED,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YACrB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;SAC7C;QAED,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,UAAkB;QAC3C,IAAI,GAAG,EAAE,GAAG,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,CAAC;QACpB,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE;YAC5B,IAAI,UAAU,CAAC,MAAM,GAAG,OAAO,EAAE;gBAC/B,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;gBACnC,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;aAC3D;iBAAM;gBACL,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;gBAC7C,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aAC9B;YACD,IAAI,CAAC,GAAG;gBACN,GAAG,GAAG,IAAI,mBAAS,CAAC,GAAG,CAAC,CAAC;;gBAEzB,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC;YACpB,2BAA2B;YAC3B,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC;YACvB,GAAG,CAAC,GAAG,EAAE,CAAC;YACV,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACrC,YAAY,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YACjD,IAAI,kBAAkB,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACzD,OAAO,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,WAAW,CAAC,SAAS,KAAK,UAAU,CAAC;YAC1F,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC;YAC5B;;;cAGE;YACF,kBAAkB,GAAG,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACtD,IAAI,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA,UAAU;YACtD,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1F,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC,CAAC;YACrE,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;YAClE,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;SACrD;QACD,OAAO;IACT,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,WAAW,GAAG,KAAK;QAC7B,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QAEnB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YACjB,OAAO;SACR;QAED,IAAI,CAAC,WAAW,EAAE;YAChB,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;gBAC7B,OAAO,EAAE,IAAI,CAAC,QAAQ;aACvB,CAAC,CAAC;SACJ;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAEpC,IAAI,IAAI,CAAC,YAAY,IAAI,SAAS,EAAE;YAClC,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;SAC/B;QAED,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,IAAI;QACJ,MAAM,YAAY,GAAG,IAAI,CAAC;QAC1B,IAAI;QACJ,MAAM,iBAAiB,GAAG,IAAI,CAAC;QAC/B,KAAK;QACL,MAAM,IAAI,GAAG,EAAE,CAAC;QAEhB,OAAO,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE;YAC1B,0BAA0B;YAC1B,IACE,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,SAAS;gBACtC,IAAI,CAAC,KAAK,CAAC,IAAI,YAAY,EAC3B;gBACA,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBAClC,KAAK,EAAE,CAAC;gBAER,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,uBAAuB,CAAC;aACrD;iBAAM,IAAI,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,uBAAuB,EAAE;gBAC/D,6CAA6C;gBAC7C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBACpC,KAAK,EAAE,CAAC;gBAER,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,EAAE;oBACjC,IAAI,CAAC,gBAAgB;wBACnB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;oBAErD,IAAI,IAAI,CAAC,gBAAgB,GAAG,CAAC,EAAE;wBAC7B,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;wBACtD,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;wBAC3B,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,kBAAkB,CAAC;qBAChD;yBAAM;wBACL,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC;qBACvC;iBACF;aACF;iBAAM,IAAI,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,kBAAkB,EAAE;gBAC1D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;gBACxD,KAAK,EAAE,CAAC;gBAER,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,gBAAgB,EAAE;oBACnD,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;oBAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE;wBAChC,cAAc;wBACd,IAAI,MAAM,GAAG,IAAA,qBAAc,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC;wBAE7C,yBAAyB;wBACzB,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,IAAI,aAAa,CAAC,CAAC;wBAC/E,IAAI,MAAM,IAAI,SAAS;4BAAE,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;wBAEtF,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;qBAC1D;oBACD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE;wBAChC,aAAa;wBACb,MAAM,MAAM,GAAG,IAAA,sBAAe,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC;wBAEhD,4DAA4D;wBAC5D,IAAI,MAAM,CAAC,UAAU,IAAI,GAAG,IAAI,MAAM,CAAC,YAAY,IAAI,SAAS,EAAE;4BAChE,IAAI,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,aAAa,CAAC,CAAC;4BAC9E,IAAI,MAAM,IAAI,SAAS,EAAE;gCACvB,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC;gCACvD,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC;gCACvD,MAAM,CAAC,eAAe,GAAG,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC;6BAC3D;yBACF;wBAED,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;wBAEhD,MAAM,eAAe,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;wBACpD,IAAI,CAAC,oBAAoB,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;qBAC3D;oBACD,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC;iBACvC;gBACD,qBAAqB;aACtB;iBAAM,IACL,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,SAAS;gBACtC,IAAI,CAAC,KAAK,CAAC,IAAI,iBAAiB,EAChC;gBACA,yCAAyC;gBACzC,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBAClC,KAAK,EAAE,CAAC;gBAER,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,mBAAmB,CAAC;aACjD;iBAAM,IAAI,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,mBAAmB,EAAE;gBAC3D,0BAA0B;gBAE1B,oCAAoC;gBACpC,kCAAkC;gBAClC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE;oBACrB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;iBACrC;gBACD,KAAK,EAAE,CAAC;gBAER,8EAA8E;gBAC9E,6EAA6E;gBAC7E,IACE,IAAI,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC;oBAC7B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI;oBACvD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,EACvD;oBACA,mBAAmB;oBAEnB,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;oBAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAE/B,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;oBAC3B,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBAC/B,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;oBAEtB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;;wBACrB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;wBAElC,IAAI,OAAO,KAAK,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;4BAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;4BAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;4BAEhD,IAAI,GAAG,IAAI,SAAS;gCAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;iCAE9C,IAAI,GAAG,IAAI,kBAAkB,EAAE;gCAClC,+DAA+D;gCAC/D,kCAAkC;gCAClC,0GAA0G;gCAC1G,IAAI,GAAG,IAAI,IAAI,CAAC,WAAW,EAAE;oCAC3B,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAA;oCAC9C,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAI,MAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,0CAAE,UAAU,CAAC,OAAO,CAAC,CAAA,EAAE;wCAC3E,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,4BAA4B;qCAC3D;oCACD,OAAO,CAAC,GAAG,CAAC,4BAA4B,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;iCACnE;qCAAM;oCACL,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;iCAC9B;6BACF;iCAEI;gCACH,4DAA4D;gCAC5D,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;oCACrB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;wCACpB,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;wCACpB,CAAC,CAAC,IAAI,CAAC;6BACV;4BAED,yGAAyG;4BACzG,IAAI,GAAG,CAAC,WAAW,EAAE,IAAI,gBAAgB,EAAE;gCACzC,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;6BAC7C;yBACF;oBACH,CAAC,CAAC,CAAC;oBAEH,uDAAuD;oBACvD,mBAAmB;oBACnB,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE;wBAC3B,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;wBAE/B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;wBACjE,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC;qBACvC;yBAAM;wBACL,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;wBACvB,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,oBAAoB,CAAC;qBAClD;iBACF;aACF;iBAAM,IACL,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,oBAAoB;gBACjD,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC,iBAAiB,EACjD;gBACA,kCAAkC;gBAClC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBACpC,KAAK,EAAE,CAAC;gBAER,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,IAAI,IAAI,CAAC,iBAAiB,EAAE;oBACtD,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;oBAChE,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAEtC,wBAAwB;oBACxB,IAAI,CAAC,IAAI,CACP,KAAK,EACL,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,GAAG,IAAI,EACzD,MAAM,CACP,CAAC;oBAEF,IAAI,CAAC,IAAI,CACP,UAAU,EACV,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,WAAW,EAChB,YAAY,CACb,CAAC;oBACF,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC;iBACvC;aACF;iBAAM;gBACL,kBAAkB;gBAClB,MAAM,IAAI,KAAK,CACb,iFAAiF,CAClF,CAAC;aACH;SACF,CAAC,YAAY;IAChB,CAAC;IAED,oBAAoB,CAAC,OAAe,EAAE,MAAc;QAClD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YACjB,OAAO;SACR;QAED,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,yCAAyC,OAAO,EAAE,CAAC;QAC/E,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAE9B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,UAAU;QAC5B,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;QACpB,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QACxC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QAExC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,YAAY,CAAC,IAAY,EAAE,IAAY,EAAE,MAAc;QACrD,MAAM,GAAG,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YAC5D,6BAA6B;YAC7B,GAAG,CAAC,KAAK,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED,oBAAoB;QAClB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,CAAC,CAAC;QAClB,MAAM,UAAU,GAAG,CAAC,CAAC;QACrB,MAAM,WAAW,GAAG,CAAC,CAAC,CAAC,kBAAkB;QACzC,MAAM,UAAU,GAAG,GAAG,CAAC,CAAC,kBAAkB;QAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,2BAA2B;QACjE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,WAAW,CAAC;QAC7D,MAAM,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC;QACvB,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QACjC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QACjC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QAC3C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QAC3C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QAC1C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QAE1C,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,MAAmB,EAAE,IAAY;QAClD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,UAAU,CAAC,GAAG,EAAE;gBACd,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,KAAU,EAAE,EAAE;oBAChC,IAAI,KAAK,EAAE;wBACT,MAAM,CAAC,KAAK,CAAC,CAAC;qBACf;yBAAM;wBACL,OAAO,CAAC,SAAS,CAAC,CAAC;qBACpB;gBACH,CAAC,CAAC,CAAA;YACJ,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC,CAAC,CAAA;IACJ,CAAC;IAEO,mBAAmB,CAAC,WAAmB,EAAE,GAAY;QAE3D,IAAI,CAAC,GAAG,EAAE;YACR,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC;SACjB;QAED,IAAI,UAAU,GAAG,EAAE,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ,EAAE;YACtC,wBAAwB;YAExB,uCAAuC;YACvC,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC,oBAAa,CAAC,CAAC,CAAC,iBAAU,CAAC,CAAC;YAE5F,MAAM,GAAG,GAAG,YAAY,CACtB,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAC9D,CAAC;YACF,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,WAAW,IAAI,GAAG,EAAE,CAAC,CAAC;YAClD,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC;YAEpE,iJAAiJ;YACjJ,IAAI,IAAI,CAAC,WAAW,CAAC,SAAS,IAAI,KAAK;gBACrC,UAAU,GAAG,oBAAoB,IAAI,CAAC,QAAQ,YAAY,IAAI,CAAC,WAAW,CAAC,KAAK,YAAY,IAAI,CAAC,WAAW,CAAC,KAAK,UAAU,GAAG,eAAe,GAAG,GAAG,CAAC;;gBAErJ,UAAU,GAAG,oBAAoB,IAAI,CAAC,QAAQ,YAAY,IAAI,CAAC,WAAW,CAAC,KAAK,YAAY,IAAI,CAAC,WAAW,CAAC,KAAK,eAAe,IAAI,CAAC,WAAW,CAAC,SAAS,SAAS,GAAG,eAAe,GAAG,GAAG,CAAC;SAChM;aAAM,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,OAAO,EAAE;YAC5C,uBAAuB;YACvB,wBAAwB;YACxB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CACrB,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,CACpC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACrB,UAAU,GAAG,SAAS,GAAG,EAAE,CAAC;SAC7B;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAID,yIAAyI;IACzI,gBAAgB,CAAC,MAAsB,EAAE,MAAc;QAEvD,sBAAsB;QACtB,IAAI,MAAM,CAAC,SAAS,IAAI,SAAS,IAAI,MAAM,CAAC,SAAS,IAAI,SAAS,IAAI,MAAM,CAAC,eAAe,IAAI,SAAS,IAAI,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,SAAS,EAAE;YACxJ,IAAI,gBAAgB,GAAG,MAAM,CAAC,eAAe,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,4BAA4B;YAC5G,IAAI,mBAAmB,GAAG,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW;YACxF,IAAI,wBAAwB,GAAG,mBAAmB,GAAG,gBAAgB,CAAC;YACtE,IAAI,YAAY,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,SAAS,GAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAC,EAAE,CAAC,CAAC,GAAC,IAAI,CAAC,CAAC,CAAC;YACxH,IAAI,aAAa,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,wBAAwB,GAAC,IAAI,CAAC,CAAC,CAAC;YACvF,OAAO,aAAa,CAAC;SACtB;QAED,uCAAuC;QACrC,OAAO,SAAS,CAAC;IACnB,CAAC;CAEF;AAx9BD,6BAw9BC","sourcesContent":["import * as net from \"net\";\r\nimport * as tls from \"tls\";\r\n\r\n// Union of net.Socket and tls.TLSSocket\r\ntype SocketUnion = net.Socket | tls.TLSSocket;\r\n\r\nimport * as dgram from \"dgram\";\r\nimport { parse as urlParse } from \"url\";\r\nimport { EventEmitter } from \"events\";\r\n\r\nimport * as util from \"./util\";\r\n\r\nimport {\r\n parseRTPPacket,\r\n parseRTCPPacket,\r\n getMD5Hash,\r\n getSHA256Hash,\r\n Transport,\r\n parseTransport,\r\n generateSSRC,\r\n} from \"./util\";\r\n\r\nimport * as transform from \"sdp-transform\";\r\nimport RTPPacket from \"./transports/RTPPacket\";\r\nconst RTP_AVP = \"RTP/AVP\";\r\nconst RTP_AVPF = \"RTP/AVPF\"; // Used by AV1. This is RTP with Feedback (via RTCP) to request Keyframes via RTCP\r\n\r\nconst STATUS_OK = 200;\r\nconst STATUS_UNAUTH = 401;\r\n\r\n// The WWW_AUTH is of the format\r\n// TOKEN key=value\r\n// TOKEN key1=value1,key2=value2\r\n// TOKEN key1=\"value1\",key2=value2\r\n\r\n// RegEx reminder ? = Zero or One item\r\n// * = Zero or More items\r\n// + = 1 or More items\r\n// \\s is whitespace. But we need to 'escape the slash', hence \\\\s (or put the regex in / / characters)\r\n// ?= is a lookahead\r\n\r\n// The RegEx has two 'Groups'\r\n// \r\n// Group 1 (finding the Key)\r\n// Look for one or more characters (a..z or A..Z)\r\n// then look for whitespace\r\n// then look for 'equals'\r\n// then look for whitespace\r\n// then look for an optional Quote character\r\n//\r\n// Group 2 (finding the Value) -\r\n// Look for EITHER 'look backwards for a Quote', some characters, 'lookahead for a Quote'\r\n// OR some characters until (by looking ahead) you can see that another key comes next. The lookahead is 'optinal whitespace' 'comma' 'optional whitespace' 'chars' 'optinal whitespace' 'equals'\r\n// OR some characters followed by 'optinal whitespace'\r\n\r\nconst WWW_AUTH = \"WWW-Authenticate\";\r\nconst WWW_AUTH_REGEX = new RegExp('([a-zA-Z]+)\\\\s*=\\\\s*\"?((?<=\").*?(?=\")|.*?(?=\\\\s*,?\\\\s*[a-zA-Z]+\\\\s*=)|.+[^\\\\s])', \"g\");\r\n\r\nenum ReadStates {\r\n SEARCHING,\r\n READING_RTSP_HEADER,\r\n READING_RTSP_PAYLOAD,\r\n READING_RAW_PACKET_SIZE,\r\n READING_RAW_PACKET,\r\n}\r\n\r\ntype Connection = \"udp\" | \"tcp\";\r\n\r\ntype AuthOptions = {\r\n type: \"Digest\" | \"Basic\",\r\n realm?: string,\r\n nonce?: string,\r\n algorithm?: \"MD5\" | \"SHA-256\",\r\n};\r\n\r\ntype Headers = {\r\n [key: string]: string | number | undefined;\r\n Session?: string;\r\n Location?: string;\r\n CSeq?: number;\r\n \"WWW-Authenticate\"?: string;\r\n Transport?: string;\r\n Unsupported?: string;\r\n};\r\n\r\n// Details for each Session within the RTSP Stream (eg video session, audio session, metadata session)\r\ntype Detail = {\r\n codec: string;\r\n mediaSource: ({ // cannot work out how to pull this type in\r\n type: string;\r\n port: number;\r\n protocol: string;\r\n payloads?: string | undefined;\r\n } & transform.MediaDescription); // get Type from the interface\r\n transport: Transport['parameters']; // get Type from the interface\r\n isH264: boolean; // legacy API\r\n rtpChannel: number;\r\n rtcpChannel: number;\r\n\r\n // Cache any optional RTCP Sender Report values (used to calculate Wall Clock Time)\r\n sr_ntpMSW?: number;\r\n sr_ntpLSW?: number;\r\n sr_rtptimestamp?: number;\r\n};\r\n\r\nexport default class RTSPClient extends EventEmitter {\r\n username: string;\r\n password: string;\r\n headers: { [key: string]: string };\r\n\r\n isConnected = false;\r\n closed = false;\r\n\r\n // These are all set in #connect or #_netConnect.\r\n\r\n _url?: string;\r\n _client?: SocketUnion;\r\n _cSeq = 0;\r\n _unsupportedExtensions?: string[];\r\n _authOpions?: AuthOptions;\r\n // Example: 'SessionId'[';timeout=seconds']\r\n _session?: string;\r\n _keepAliveID?: NodeJS.Timeout;\r\n _nextFreeInterleavedChannel = 0;\r\n _nextFreeUDPPort = 5000;\r\n\r\n readState: ReadStates = ReadStates.SEARCHING;\r\n\r\n // Used as a cache for the data stream.\r\n // What's in here is based on current #readState.\r\n messageBytes: number[] = [];\r\n\r\n // Used for parsing RTSP responses,\r\n\r\n // Content-Length header in the RTSP message.\r\n rtspContentLength = 0;\r\n rtspStatusLine = \"\";\r\n rtspHeaders: Headers = {};\r\n\r\n // Used for parsing RTP/RTCP responses.\r\n\r\n rtspPacketLength = 0;\r\n rtspPacket = Buffer.from(\"\");\r\n rtspPacketPointer = 0;\r\n\r\n // Used in #_emptyReceiverReport.\r\n clientSSRC = generateSSRC();\r\n\r\n tcpSocket: SocketUnion = new net.Socket();\r\n setupResult: Array = [];\r\n constructor(\r\n username: string,\r\n password: string,\r\n headers?: { [key: string]: string }\r\n ) {\r\n super();\r\n\r\n this.username = username;\r\n this.password = password;\r\n this.headers = {\r\n ...(headers || {}),\r\n \"User-Agent\": \"yellowstone/3.x\",\r\n };\r\n }\r\n\r\n // This manages the lifecycle for the RTSP connection\r\n // over TCP.\r\n //\r\n // Sets #_client.\r\n //\r\n // Handles receiving data & closing port, called during\r\n // #connect.\r\n _netConnect(hostname: string, port: number, secure: boolean = false): Promise {\r\n return new Promise((resolve, reject) => {\r\n // Set after listeners defined.\r\n\r\n const errorListener = (err: any) => {\r\n client.removeListener(\"error\", errorListener);\r\n reject(err);\r\n };\r\n\r\n const postConnectErrorListener = (err: any) => {\r\n client.removeListener(\"error\", postConnectErrorListener);\r\n this.emit(\"error\", err);\r\n reject(err);\r\n };\r\n\r\n const closeListener = () => {\r\n client.removeListener(\"close\", closeListener);\r\n this.emit(\"close\");\r\n this.close(true);\r\n };\r\n\r\n const responseListener = (responseName: string, headers: Headers) => {\r\n const name = responseName.split(\" \")[0];\r\n\r\n if (name.indexOf(\"RTSP/\") === 0) {\r\n return;\r\n }\r\n\r\n if (name === \"REDIRECT\" || name === \"ANNOUNCE\") {\r\n this.respond(\"200 OK\", { CSeq: headers.CSeq });\r\n }\r\n\r\n if (name === \"REDIRECT\" && headers.Location) {\r\n this.close();\r\n this.connect(headers.Location);\r\n }\r\n };\r\n\r\n // rtsp or rtsps(with tls)\r\n let client: SocketUnion;\r\n if (secure == false) {\r\n client = net.connect(port, hostname, () => {\r\n this.isConnected = true;\r\n this._client = client;\r\n\r\n client.removeListener(\"error\", errorListener);\r\n client.on(\"error\", postConnectErrorListener);\r\n\r\n this.on(\"response\", responseListener);\r\n resolve(this);\r\n });\r\n }\r\n else {\r\n const options: tls.ConnectionOptions = {\r\n rejectUnauthorized: false\r\n };\r\n client = tls.connect(port, hostname, options, () => {\r\n console.log(\"TLS Connection\");\r\n this.isConnected = true;\r\n this._client = client;\r\n\r\n client.removeListener(\"error\", errorListener);\r\n\r\n this.on(\"response\", responseListener);\r\n resolve(this);\r\n });\r\n }\r\n\r\n client.on(\"data\", this._onData.bind(this));\r\n client.on(\"error\", errorListener);\r\n client.on(\"close\", closeListener);\r\n this.tcpSocket = client;\r\n });\r\n }\r\n\r\n async connect(\r\n url: string,\r\n {\r\n keepAlive = true,\r\n connection = \"udp\",\r\n secure = false,\r\n }: { keepAlive: boolean; connection?: Connection, secure: boolean } = {\r\n keepAlive: true,\r\n connection: \"udp\",\r\n secure: false\r\n }\r\n ): Promise {\r\n const { hostname, port } = urlParse((this._url = url));\r\n if (!hostname) {\r\n throw new Error(\"URL parsing error in connect method.\");\r\n }\r\n\r\n const details: Detail[] = [];\r\n\r\n await this._netConnect(hostname, parseInt(port || \"554\"), secure);\r\n await this.request(\"OPTIONS\");\r\n\r\n const describeRes = await this.request(\"DESCRIBE\", {\r\n Accept: \"application/sdp\",\r\n });\r\n if (!describeRes || !describeRes.mediaHeaders) {\r\n throw new Error(\r\n \"No media headers on DESCRIBE; RTSP server is broken (sanity check)\"\r\n );\r\n }\r\n\r\n // For now, only RTP/AVP and RTP/AVPF are supported. (Some RTSPS servers use RTP/SAVP)\r\n const { media } = transform.parse(describeRes.mediaHeaders.join(\"\\r\\n\"));\r\n\r\n // Loop over the Media Streams in the SDP looking for Video or Audio\r\n // In theory the SDP can contain multiple Video and Audio Streams. We only want one of each type\r\n let hasVideo = false;\r\n let hasAudio = false;\r\n let hasMetaData = false;\r\n let hasBackchannel = false;\r\n\r\n for (let x = 0; x < media.length; x++) {\r\n let needSetup = false;\r\n let codec = \"\";\r\n const mediaSource = media[x];\r\n\r\n\r\n // RFC says \"If none of the direction attributes (\"sendonly\", \"recvonly\", \"inactive\", and \"sendrecv\") are present,\r\n // the \"sendrecv\" SHOULD be assumed\r\n if (mediaSource.direction == undefined) mediaSource.direction = \"sendrecv\"; // Wowza does not send 'direction'\r\n\r\n if (\r\n mediaSource.type === \"video\" &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec === \"H264\"\r\n ) {\r\n this.emit(\"log\", \"H264 Video Stream Found in SDP\", \"\");\r\n if (hasVideo == false) {\r\n needSetup = true;\r\n hasVideo = true;\r\n codec = \"H264\";\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"video\" &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec === \"H265\"\r\n ) {\r\n this.emit(\"log\", \"H265 Video Stream Found in SDP\", \"\");\r\n if (hasVideo == false) {\r\n needSetup = true;\r\n hasVideo = true;\r\n codec = \"H265\";\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"video\" &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec === \"H266\"\r\n ) {\r\n this.emit(\"log\", \"H266 Video Stream Found in SDP\", \"\");\r\n if (hasVideo == false) {\r\n needSetup = true;\r\n hasVideo = true;\r\n codec = \"H266\";\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"video\" &&\r\n (mediaSource.protocol === RTP_AVP || mediaSource.protocol === RTP_AVPF) &&\r\n mediaSource.rtp[0].codec === \"AV1\"\r\n ) {\r\n this.emit(\"log\", \"AV1 Video Stream Found in SDP\", \"\");\r\n if (hasVideo == false) {\r\n needSetup = true;\r\n hasVideo = true;\r\n codec = \"AV1\";\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"audio\" &&\r\n (mediaSource.direction === \"recvonly\" || mediaSource.direction === \"sendrecv\") &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec.toLowerCase() === \"mpeg4-generic\" && // (RFC examples are lower case. Axis cameras use upper case)\r\n mediaSource.fmtp[0].config.includes(\"AAC\")\r\n ) {\r\n this.emit(\"log\", \"AAC Audio Stream Found in SDP\", \"\");\r\n if (hasAudio == false) {\r\n needSetup = true;\r\n hasAudio = true;\r\n codec = \"AAC\";\r\n }\r\n }\r\n\r\n if (mediaSource.type === \"audio\" &&\r\n mediaSource.direction === \"sendonly\" &&\r\n mediaSource.protocol === RTP_AVP) {\r\n this.emit(\"log\", \"Audio backchannel Found in SDP\", \"\");\r\n if (hasBackchannel == false) {\r\n needSetup = true;\r\n hasBackchannel = true;\r\n codec = mediaSource.rtp[0].codec;\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"application\" &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec.toLowerCase() === \"vnd.onvif.metadata\"\r\n ) {\r\n this.emit(\"log\", \"ONVIF Meta Data Found in SDP\", \"\");\r\n if (hasMetaData == false) {\r\n needSetup = true;\r\n hasMetaData = true;\r\n codec = \"vnd.onvif.metadata\";\r\n }\r\n }\r\n\r\n if (needSetup) {\r\n let streamurl = \"\";\r\n // The 'control' in the SDP can be a relative or absolute uri\r\n if (mediaSource.control) {\r\n if (mediaSource.control.toLowerCase().startsWith(\"rtsp://\")) {\r\n // absolute path\r\n streamurl = mediaSource.control;\r\n } else {\r\n // relative path\r\n streamurl = this._url + \"/\" + mediaSource.control;\r\n }\r\n }\r\n\r\n // Perform a SETUP on the streamurl\r\n // either 'udp' RTP/RTCP packets\r\n // or with 'tcp' RTP/TCP packets which are interleaved into the TCP based RTSP socket\r\n let setupRes;\r\n let rtpChannel: number;\r\n let rtcpChannel: number;\r\n let rtpReceiver: dgram.Socket|null = null; // UDP mode init value\r\n let rtcpReceiver: dgram.Socket|null = null; // UDP mode init value\r\n\r\n if (connection === \"udp\") {\r\n // Create a pair of UDP listeners, even numbered port for RTP\r\n // and odd numbered port for RTCP\r\n\r\n rtpChannel = this._nextFreeUDPPort;\r\n rtcpChannel = this._nextFreeUDPPort + 1;\r\n this._nextFreeUDPPort += 2;\r\n\r\n const rtpPort = rtpChannel;\r\n rtpReceiver = dgram.createSocket(\"udp4\");\r\n\r\n rtpReceiver.on(\"message\", (buf, remote) => {\r\n let packet = parseRTPPacket(buf);\r\n\r\n // Add wall clock time\r\n const detail = this.setupResult.find(item => item.rtpChannel == rtpChannel);\r\n if (detail != undefined) packet.wallclockTime = this.GetWallClockTime(packet, detail);\r\n\r\n this.emit(\"data\", rtpPort, packet.payload, packet);\r\n });\r\n\r\n const rtcpPort = rtcpChannel;\r\n rtcpReceiver = dgram.createSocket(\"udp4\");\r\n\r\n rtcpReceiver.on(\"message\", (buf, remote) => {\r\n const packet = parseRTCPPacket(buf);\r\n\r\n // If this is a Sender Report, cache the NTP Wall Clock data\r\n if (packet.packetType == 200 && packet.senderReport != undefined) {\r\n let detail = this.setupResult.find(item => item.rtcpChannel == rtcpChannel);\r\n if (detail != undefined) {\r\n detail.sr_ntpMSW = packet.senderReport.ntpTimestampMSW;\r\n detail.sr_ntpLSW = packet.senderReport.ntpTimestampLSW;\r\n detail.sr_rtptimestamp = packet.senderReport.rtpTimestamp;\r\n }\r\n }\r\n\r\n this.emit(\"controlData\", rtcpPort, packet);\r\n\r\n const receiver_report = this._emptyReceiverReport();\r\n this._sendUDPData(remote.address, remote.port, receiver_report);\r\n });\r\n\r\n // Block until both UDP sockets are open.\r\n\r\n await new Promise((resolve) => {\r\n rtpReceiver?.bind(rtpPort, () => resolve({}));\r\n });\r\n\r\n await new Promise((resolve) => {\r\n rtcpReceiver?.bind(rtcpPort, () => resolve({}));\r\n });\r\n\r\n const setupHeader = {\r\n Transport: `RTP/AVP;unicast;client_port=${rtpPort}-${rtcpPort}`,\r\n };\r\n if (this._session)\r\n Object.assign(setupHeader, { Session: this._session });\r\n setupRes = await this.request(\"SETUP\", setupHeader, streamurl);\r\n } else if (connection === \"tcp\") {\r\n // channel 0, RTP\r\n // channel 1, RTCP\r\n\r\n rtpChannel = this._nextFreeInterleavedChannel;\r\n rtcpChannel = this._nextFreeInterleavedChannel + 1;\r\n this._nextFreeInterleavedChannel += 2;\r\n\r\n const setupHeader = {\r\n Transport: `RTP/AVP/TCP;interleaved=${rtpChannel}-${rtcpChannel}`,\r\n };\r\n if (this._session)\r\n Object.assign(setupHeader, { Session: this._session }); // not used on first SETUP\r\n setupRes = await this.request(\"SETUP\", setupHeader, streamurl);\r\n } else {\r\n throw new Error(\r\n `Connection parameter to RTSPClient#connect is ${connection}, not udp or tcp!`\r\n );\r\n }\r\n\r\n if (!setupRes) {\r\n throw new Error(\r\n \"No SETUP response; RTSP server is broken (sanity check)\"\r\n );\r\n }\r\n\r\n const { headers } = setupRes;\r\n\r\n if (!headers.Transport) {\r\n throw new Error(\r\n \"No Transport header on SETUP; RTSP server is broken (sanity check)\"\r\n );\r\n }\r\n\r\n const transport = parseTransport(headers.Transport);\r\n if (\r\n // TCP\r\n transport.protocol !== \"RTP/AVP/TCP\" &&\r\n // UDP\r\n transport.protocol !== \"RTP/AVP\" &&\r\n // UDP\r\n transport.protocol !== \"RTP/AVP/UDP\" // Panasonic cameras send this\r\n ) {\r\n throw new Error(\r\n \"Only RTSP servers supporting RTP/AVP or RTP/AVP/UDP or RTP/AVP/TCP are supported at this time.\"\r\n );\r\n }\r\n\r\n // Patch from zoolyka (Zoltan Hajdu).\r\n // Try to open a hole in the NAT router (to allow incoming UDP packets)\r\n // by send a UDP packet for RTP and RTCP to the remote RTSP server.\r\n // Note, Roger did not have a router that needed this so the feature is untested.\r\n // May be better to change the RTCP message to a Receiver Report, leaving the RTP message as zero bytes\r\n if (connection === \"udp\" && transport && rtpReceiver && rtcpReceiver) {\r\n rtpReceiver.send(Buffer.from(''), Number(transport.parameters[\"server_port\"].split(\"-\")[0]), hostname);\r\n rtcpReceiver.send(Buffer.from(''), Number(transport.parameters[\"server_port\"].split(\"-\")[1]), hostname);\r\n }\r\n\r\n if (headers.Unsupported) {\r\n this._unsupportedExtensions = headers.Unsupported.split(\",\");\r\n }\r\n\r\n if (headers.Session) {\r\n this._session = headers.Session.split(\";\")[0];\r\n }\r\n\r\n const detail: Detail = {\r\n codec,\r\n mediaSource,\r\n transport: transport.parameters,\r\n isH264: codec === \"H264\", // legacy API\r\n rtpChannel,\r\n rtcpChannel,\r\n };\r\n\r\n details.push(detail);\r\n } // end if (needSetup)\r\n } // end for loop, looping over each media stream\r\n\r\n if (keepAlive) {\r\n // Start a Timer to send OPTIONS every 20 seconds to keep stream alive\r\n // using the Session ID\r\n this._keepAliveID = setInterval(() => {\r\n this.request(\"OPTIONS\", { Session: this._session });\r\n // this.request(\"OPTIONS\");\r\n }, 20 * 1000);\r\n }\r\n\r\n this.setupResult = details;\r\n return details;\r\n }\r\n\r\n request(\r\n requestName: string,\r\n headersParam: Headers = {},\r\n url?: string\r\n ): Promise<{ headers: Headers; mediaHeaders?: string[] } | void> {\r\n if (!this._client) {\r\n return Promise.resolve();\r\n }\r\n\r\n if (!url) {\r\n url = this._url;\r\n }\r\n\r\n const id = ++this._cSeq;\r\n // mutable via string addition\r\n let req = `${requestName} ${url} RTSP/1.0\\r\\nCSeq: ${id}\\r\\n`;\r\n\r\n const headers = {\r\n ...this.headers,\r\n ...headersParam,\r\n };\r\n\r\n if (this._authOpions) {\r\n Object.assign(headers, {\r\n Authorization: this._generateAuthString(requestName, url),\r\n });\r\n }\r\n\r\n // NOTE:\r\n // If we cache the Authenitcation Type (Direct or Basic) then we could\r\n // re-compute an Authorization Header here and include in the RTSP Command\r\n // This would make connections a faster with fewer round-trips to the RTSP Server\r\n\r\n req += Object.entries(headers)\r\n .map(([key, value]) => `${key}: ${value}\\r\\n`)\r\n .join(\"\");\r\n\r\n this.emit(\"log\", req, \"C->S\");\r\n // Make sure to add an empty line after the request.\r\n this._client.write(`${req}\\r\\n`);\r\n\r\n return new Promise((resolve, reject) => {\r\n const responseHandler = (\r\n responseName: string,\r\n resHeaders: Headers,\r\n mediaHeaders: string[]\r\n ) => {\r\n const firstAnswer: string = String(resHeaders[\"\"]) || \"\";\r\n if (firstAnswer.indexOf(\"401\") >= 0 && 'Authorization' in headers) {\r\n // If the RTSP Command we sent included an Authorization and we have 401 error, then reject()\r\n reject(new Error(`Bad RTSP credentials!`));\r\n return;\r\n }\r\n if (resHeaders.CSeq !== id) {\r\n return;\r\n }\r\n\r\n this.removeListener(\"response\", responseHandler);\r\n\r\n const statusCode = parseInt(responseName.split(\" \")[1]);\r\n\r\n if (statusCode === STATUS_OK) {\r\n if (mediaHeaders.length > 0) {\r\n resolve({\r\n headers: resHeaders,\r\n mediaHeaders,\r\n });\r\n } else {\r\n resolve({\r\n headers: resHeaders,\r\n });\r\n }\r\n } else {\r\n const authHeader = resHeaders[WWW_AUTH];\r\n\r\n // We have status code unauthenticated.\r\n if (statusCode === STATUS_UNAUTH && authHeader) {\r\n this._authOpions = {\r\n type: authHeader.split(\" \")[0] as AuthOptions[\"type\"],\r\n algorithm: \"MD5\", // Default to MD5 if no algorthm is given. Milestone's RTSP server also supports SHA-256 for FIPS\r\n }\r\n\r\n // Get auth properties from WWW_AUTH header.\r\n let match = WWW_AUTH_REGEX.exec(authHeader);\r\n while (match != null) {\r\n const prop = match[1];\r\n\r\n if (prop == \"realm\" && match[2]) {\r\n this._authOpions.realm = match[2];\r\n }\r\n\r\n if (prop == \"nonce\" && match[2]) {\r\n this._authOpions.nonce = match[2];\r\n }\r\n\r\n if (prop == \"algorithm\" && match[2]) {\r\n this._authOpions.algorithm = match[2] as AuthOptions[\"algorithm\"];\r\n }\r\n\r\n match = WWW_AUTH_REGEX.exec(authHeader);\r\n }\r\n\r\n // Repeat the request, now _authOptions will be detected and the Authorization header will be generated\r\n resolve(this.request(requestName, headers, url));\r\n return;\r\n }\r\n\r\n reject(new Error(`Bad RTSP status code ${statusCode}!`));\r\n return;\r\n }\r\n };\r\n\r\n this.on(\"response\", responseHandler);\r\n });\r\n }\r\n\r\n respond(status: string, headersParam: Headers = {}): void {\r\n if (!this._client) {\r\n return;\r\n }\r\n\r\n // mutable via string addition\r\n let res = `RTSP/1.0 ${status}\\r\\n`;\r\n\r\n const headers = {\r\n ...this.headers,\r\n ...headersParam,\r\n };\r\n\r\n res += Object.entries(headers)\r\n .map(([key, value]) => `${key}: ${value}\\r\\n`)\r\n .join(\"\");\r\n\r\n this.emit(\"log\", res, \"C->S\");\r\n this._client.write(`${res}\\r\\n`);\r\n }\r\n\r\n async play(): Promise {\r\n if (!this.isConnected) {\r\n throw new Error(\"Client is not connected.\");\r\n }\r\n\r\n await this.request(\"PLAY\", { Session: this._session });\r\n }\r\n\r\n async pause(): Promise {\r\n if (!this.isConnected) {\r\n throw new Error(\"Client is not connected.\");\r\n }\r\n\r\n await this.request(\"PAUSE\", { Session: this._session });\r\n }\r\n\r\n async sendAudioBackChannel(audioChunk: Buffer): Promise {\r\n let rtp, buf;\r\n const bufSize = 160;\r\n while (audioChunk.length > 0) {\r\n if (audioChunk.length > bufSize) {\r\n buf = audioChunk.slice(0, bufSize);\r\n audioChunk = audioChunk.slice(bufSize, audioChunk.length);\r\n } else {\r\n buf = audioChunk.slice(0, audioChunk.length);\r\n audioChunk = Buffer.from([]);\r\n }\r\n if (!rtp)\r\n rtp = new RTPPacket(buf);\r\n else\r\n rtp.payload = buf;\r\n // rtp.type = 8;// set động\r\n rtp.time += buf.length;\r\n rtp.seq++;\r\n const bufferLength = Buffer.alloc(2);\r\n bufferLength.writeUInt16BE(rtp.packet.length, 0);\r\n let channelInterleaved = this.setupResult.filter((value) => {\r\n return value.mediaSource.type === 'audio' && value.mediaSource.direction === 'sendonly';\r\n })[0].transport.interleaved;\r\n /* RTSP Interleaved Frame structure\r\n |dollar sign|channel identifier|data length|\r\n |1 Byte |1 Byte |2 Bytes |\r\n */\r\n channelInterleaved = channelInterleaved.split('-')[0];\r\n let interleavedHeader = Buffer.from([0x24]);// set '$'\r\n interleavedHeader = Buffer.concat([interleavedHeader, Buffer.from([channelInterleaved])]);\r\n interleavedHeader = Buffer.concat([interleavedHeader, bufferLength]);\r\n const dataToSend = Buffer.concat([interleavedHeader, rtp.packet]);\r\n await this._socketWrite(this.tcpSocket, dataToSend);\r\n }\r\n return;\r\n }\r\n\r\n async close(isImmediate = false): Promise {\r\n if (this.closed) return;\r\n this.closed = true;\r\n\r\n if (!this._client) {\r\n return;\r\n }\r\n\r\n if (!isImmediate) {\r\n await this.request(\"TEARDOWN\", {\r\n Session: this._session,\r\n });\r\n }\r\n\r\n this._client.end();\r\n this.removeAllListeners(\"response\");\r\n\r\n if (this._keepAliveID != undefined) {\r\n clearInterval(this._keepAliveID);\r\n this._keepAliveID = undefined;\r\n }\r\n\r\n this.isConnected = false;\r\n this._cSeq = 0;\r\n }\r\n\r\n _onData(data: Buffer): void {\r\n let index = 0;\r\n\r\n // $\r\n const PACKET_START = 0x24;\r\n // R\r\n const RTSP_HEADER_START = 0x52;\r\n // /n\r\n const ENDL = 10;\r\n\r\n while (index < data.length) {\r\n // read RTP or RTCP packet\r\n if (\r\n this.readState == ReadStates.SEARCHING &&\r\n data[index] == PACKET_START\r\n ) {\r\n this.messageBytes = [data[index]];\r\n index++;\r\n\r\n this.readState = ReadStates.READING_RAW_PACKET_SIZE;\r\n } else if (this.readState == ReadStates.READING_RAW_PACKET_SIZE) {\r\n // accumulate bytes for $, channel and length\r\n this.messageBytes.push(data[index]);\r\n index++;\r\n\r\n if (this.messageBytes.length == 4) {\r\n this.rtspPacketLength =\r\n (this.messageBytes[2] << 8) + this.messageBytes[3];\r\n\r\n if (this.rtspPacketLength > 0) {\r\n this.rtspPacket = Buffer.alloc(this.rtspPacketLength);\r\n this.rtspPacketPointer = 0;\r\n this.readState = ReadStates.READING_RAW_PACKET;\r\n } else {\r\n this.readState = ReadStates.SEARCHING;\r\n }\r\n }\r\n } else if (this.readState == ReadStates.READING_RAW_PACKET) {\r\n this.rtspPacket[this.rtspPacketPointer++] = data[index];\r\n index++;\r\n\r\n if (this.rtspPacketPointer == this.rtspPacketLength) {\r\n const packetChannel = this.messageBytes[1];\r\n if ((packetChannel & 0x01) === 0) {\r\n // even number\r\n let packet = parseRTPPacket(this.rtspPacket);\r\n\r\n // Get the Session Detail\r\n const detail = this.setupResult.find(item => item.rtpChannel == packetChannel);\r\n if (detail != undefined) packet.wallclockTime = this.GetWallClockTime(packet, detail);\r\n\r\n this.emit(\"data\", packetChannel, packet.payload, packet);\r\n }\r\n if ((packetChannel & 0x01) === 1) {\r\n // odd number\r\n const packet = parseRTCPPacket(this.rtspPacket);\r\n\r\n // If this is a Sender Report, cache the NTP Wall Clock data\r\n if (packet.packetType == 200 && packet.senderReport != undefined) {\r\n let detail = this.setupResult.find(item => item.rtcpChannel == packetChannel);\r\n if (detail != undefined) {\r\n detail.sr_ntpMSW = packet.senderReport.ntpTimestampMSW;\r\n detail.sr_ntpLSW = packet.senderReport.ntpTimestampLSW;\r\n detail.sr_rtptimestamp = packet.senderReport.rtpTimestamp;\r\n }\r\n }\r\n \r\n this.emit(\"controlData\", packetChannel, packet);\r\n\r\n const receiver_report = this._emptyReceiverReport();\r\n this._sendInterleavedData(packetChannel, receiver_report);\r\n }\r\n this.readState = ReadStates.SEARCHING;\r\n }\r\n // read response data\r\n } else if (\r\n this.readState == ReadStates.SEARCHING &&\r\n data[index] == RTSP_HEADER_START\r\n ) {\r\n // found the start of a RTSP rtsp_message\r\n this.messageBytes = [data[index]];\r\n index++;\r\n\r\n this.readState = ReadStates.READING_RTSP_HEADER;\r\n } else if (this.readState == ReadStates.READING_RTSP_HEADER) {\r\n // Reading a RTSP message.\r\n\r\n // Add character to the messageBytes\r\n // Ignore /r (13) but keep /n (10)\r\n if (data[index] != 13) {\r\n this.messageBytes.push(data[index]);\r\n }\r\n index++;\r\n\r\n // if we have two new lines back to back then we have a complete RTSP command,\r\n // note we may still need to read the Content Payload (the body) e.g. the SDP\r\n if (\r\n this.messageBytes.length >= 2 &&\r\n this.messageBytes[this.messageBytes.length - 2] == ENDL &&\r\n this.messageBytes[this.messageBytes.length - 1] == ENDL\r\n ) {\r\n // Parse the Header\r\n\r\n const text = String.fromCharCode.apply(null, this.messageBytes);\r\n const lines = text.split(\"\\n\");\r\n\r\n this.rtspContentLength = 0;\r\n this.rtspStatusLine = lines[0];\r\n this.rtspHeaders = {};\r\n\r\n lines.forEach((line) => {\r\n const indexOf = line.indexOf(\":\");\r\n\r\n if (indexOf !== line.length - 1) {\r\n const key = line.substring(0, indexOf).trim();\r\n const data = line.substring(indexOf + 1).trim();\r\n\r\n if (key == \"Session\") this.rtspHeaders[key] = data;\r\n\r\n else if (key == \"WWW-Authenticate\") {\r\n // Handle multiple WWW-Authenticate entries and pick the 'best'\r\n // We prefer 'Digest' over 'Basic'\r\n // We preger 'Digest SHAxxx' over 'Digest MD5' or 'Digest with no algorithm (defaults to MD5) (STILL TODO)\r\n if (key in this.rtspHeaders) {\r\n console.log(\"Duplicate WWW-Authenticate keys\")\r\n if (data.startsWith(\"Digest\") && this.rtspHeaders[key]?.startsWith(\"Basic\")) {\r\n this.rtspHeaders[key] = data; // Replace Basic with Digest\r\n }\r\n console.log(\"Keeping WWW-Authenticate: \" + this.rtspHeaders[key]);\r\n } else {\r\n this.rtspHeaders[key] = data;\r\n }\r\n }\r\n\r\n else {\r\n // Store the result as either a String type or a Number type\r\n this.rtspHeaders[key] =\r\n data.match(/^[0-9]+$/)\r\n ? parseInt(data, 10)\r\n : data;\r\n }\r\n\r\n // workaround for buggy Hipcam RealServer/V1.0 camera which returns Content-length and not Content-Length\r\n if (key.toLowerCase() == \"content-length\") {\r\n this.rtspContentLength = parseInt(data, 10);\r\n }\r\n }\r\n });\r\n\r\n // if no content length, there there's no media headers\r\n // emit the message\r\n if (!this.rtspContentLength) {\r\n this.emit(\"log\", text, \"S->C\");\r\n\r\n this.emit(\"response\", this.rtspStatusLine, this.rtspHeaders, []);\r\n this.readState = ReadStates.SEARCHING;\r\n } else {\r\n this.messageBytes = [];\r\n this.readState = ReadStates.READING_RTSP_PAYLOAD;\r\n }\r\n }\r\n } else if (\r\n this.readState == ReadStates.READING_RTSP_PAYLOAD &&\r\n this.messageBytes.length < this.rtspContentLength\r\n ) {\r\n // Copy data into the RTSP payload\r\n this.messageBytes.push(data[index]);\r\n index++;\r\n\r\n if (this.messageBytes.length == this.rtspContentLength) {\r\n const text = String.fromCharCode.apply(null, this.messageBytes);\r\n const mediaHeaders = text.split(\"\\n\");\r\n\r\n // Emit the RTSP message\r\n this.emit(\r\n \"log\",\r\n String.fromCharCode.apply(null, this.messageBytes) + text,\r\n \"S->C\"\r\n );\r\n\r\n this.emit(\r\n \"response\",\r\n this.rtspStatusLine,\r\n this.rtspHeaders,\r\n mediaHeaders\r\n );\r\n this.readState = ReadStates.SEARCHING;\r\n }\r\n } else {\r\n // unexpected data\r\n throw new Error(\r\n \"Bug in RTSP data framing, please file an issue with the author with stacktrace.\"\r\n );\r\n }\r\n } // end while\r\n }\r\n\r\n _sendInterleavedData(channel: number, buffer: Buffer): void {\r\n if (!this._client) {\r\n return;\r\n }\r\n\r\n const req = `${buffer.length} bytes of interleaved data on channel ${channel}`;\r\n this.emit(\"log\", req, \"C->S\");\r\n\r\n const header = Buffer.alloc(4);\r\n header[0] = 0x24; // ascii $\r\n header[1] = channel;\r\n header[2] = (buffer.length >> 8) & 0xff;\r\n header[3] = (buffer.length >> 0) & 0xff;\r\n\r\n const data = Buffer.concat([header, buffer]);\r\n this._client.write(data);\r\n }\r\n\r\n _sendUDPData(host: string, port: number, buffer: Buffer): void {\r\n const udp = dgram.createSocket(\"udp4\");\r\n udp.send(buffer, 0, buffer.length, port, host, (err, bytes) => {\r\n // TODO: Don't ignore errors.\r\n udp.close();\r\n });\r\n }\r\n\r\n _emptyReceiverReport(): Buffer {\r\n const report = Buffer.alloc(8);\r\n const version = 2;\r\n const paddingBit = 0;\r\n const reportCount = 0; // an empty report\r\n const packetType = 201; // Receiver Report\r\n const length = report.length / 4 - 1; // num 32 bit words minus 1\r\n report[0] = (version << 6) + (paddingBit << 5) + reportCount;\r\n report[1] = packetType;\r\n report[2] = (length >> 8) & 0xff;\r\n report[3] = (length >> 0) & 0xff;\r\n report[4] = (this.clientSSRC >> 24) & 0xff;\r\n report[5] = (this.clientSSRC >> 16) & 0xff;\r\n report[6] = (this.clientSSRC >> 8) & 0xff;\r\n report[7] = (this.clientSSRC >> 0) & 0xff;\r\n\r\n return report;\r\n }\r\n\r\n async _socketWrite(socket: SocketUnion, data: Buffer): Promise {\r\n return new Promise((resolve, reject) => {\r\n setTimeout(() => {\r\n socket.write(data, (error: any) => {\r\n if (error) {\r\n reject(error);\r\n } else {\r\n resolve(undefined);\r\n }\r\n })\r\n }, 20);\r\n })\r\n }\r\n\r\n private _generateAuthString(requestName: string, url?: string): string {\r\n\r\n if (!url) {\r\n url = this._url;\r\n }\r\n\r\n let authString = \"\";\r\n if (!this._authOpions) return \"\";\r\n if (this._authOpions.type === \"Digest\") {\r\n // Digest Authentication\r\n\r\n // Select Hash Function, default to MD5\r\n const HashFunction = (this._authOpions.algorithm == \"SHA-256\" ? getSHA256Hash : getMD5Hash);\r\n\r\n const ha1 = HashFunction(\r\n `${this.username}:${this._authOpions.realm}:${this.password}`\r\n );\r\n const ha2 = HashFunction(`${requestName}:${url}`);\r\n const ha3 = HashFunction(`${ha1}:${this._authOpions.nonce}:${ha2}`);\r\n\r\n // Some RTSP servers to not accept \"algorithm=NNN\" in the authString and reject the authentication. So only add algorithm=ZZZZ when not using MD5\r\n if (this._authOpions.algorithm == \"MD5\")\r\n authString = `Digest username=\"${this.username}\",realm=\"${this._authOpions.realm}\",nonce=\"${this._authOpions.nonce}\",uri=\"${url}\",response=\"${ha3}\"`;\r\n else\r\n authString = `Digest username=\"${this.username}\",realm=\"${this._authOpions.realm}\",nonce=\"${this._authOpions.nonce}\",algorithm=${this._authOpions.algorithm},uri=\"${url}\",response=\"${ha3}\"`;\r\n } else if (this._authOpions.type === \"Basic\") {\r\n // Basic Authentication\r\n // https://xkcd.com/538/\r\n const b64 = Buffer.from(\r\n `${this.username}:${this.password}`\r\n ).toString(\"base64\");\r\n authString = `Basic ${b64}`;\r\n }\r\n return authString;\r\n }\r\n\r\n ntpBaseDate_ms = new Date(\"1900/1/1\").getTime();\r\n\r\n // Note we have had a RTP Packet in Yellowstone for many years, but the Audio Backchennal code added another object also called RTPPacket\r\n GetWallClockTime(packet: util.RTPPacket, detail: Detail): Date | undefined {\r\n\r\n // Add Wall Clock Time\r\n if (detail.sr_ntpMSW != undefined && detail.sr_ntpLSW != undefined && detail.sr_rtptimestamp != undefined && detail.mediaSource.rtp[0].rate != undefined) {\r\n let refTimestampSecs = detail.sr_rtptimestamp / detail.mediaSource.rtp[0].rate; // H264 is 90 kHz clock rate\r\n let packetTimestampSecs = packet.timestamp / detail.mediaSource.rtp[0].rate; // eg 90kHz\r\n let packetTimestampDeltaSecs = packetTimestampSecs - refTimestampSecs;\r\n let refTimestamp = new Date(this.ntpBaseDate_ms + (detail.sr_ntpMSW * 1000) + ((detail.sr_ntpLSW/Math.pow(2,32))*1000));\r\n let wallclockTime = new Date(refTimestamp.getTime() + (packetTimestampDeltaSecs*1000));\r\n return wallclockTime;\r\n }\r\n\r\n // Could not generate a Wall Clock Time\r\n return undefined;\r\n }\r\n\r\n}\r\n\r\nexport { RTPPacket, RTCPPacket } from \"./util\";\r\n"]} \ No newline at end of file +{"version":3,"file":"RTSPClient.js","sourceRoot":"","sources":["../lib/RTSPClient.ts"],"names":[],"mappings":"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","sourcesContent":["import * as net from \"net\";\r\nimport * as tls from \"tls\";\r\n\r\n// Union of net.Socket and tls.TLSSocket\r\ntype SocketUnion = net.Socket | tls.TLSSocket;\r\n\r\nimport * as dgram from \"dgram\";\r\nimport { parse as urlParse } from \"url\";\r\nimport { EventEmitter } from \"events\";\r\n\r\nimport * as util from \"./util\";\r\n\r\nimport {\r\n parseRTPPacket,\r\n parseRTCPPacket,\r\n getMD5Hash,\r\n getSHA256Hash,\r\n Transport,\r\n parseTransport,\r\n generateSSRC,\r\n} from \"./util\";\r\n\r\nimport * as transform from \"sdp-transform\";\r\nimport RTPPacket from \"./transports/RTPPacket\";\r\nconst RTP_AVP = \"RTP/AVP\";\r\nconst RTP_AVPF = \"RTP/AVPF\"; // Used by AV1. This is RTP with Feedback (via RTCP) to request Keyframes via RTCP\r\n\r\nconst STATUS_OK = 200;\r\nconst STATUS_UNAUTH = 401;\r\n\r\n// The WWW_AUTH is of the format\r\n// TOKEN key=value\r\n// TOKEN key1=value1,key2=value2\r\n// TOKEN key1=\"value1\",key2=value2\r\n\r\n// RegEx reminder ? = Zero or One item\r\n// * = Zero or More items\r\n// + = 1 or More items\r\n// \\s is whitespace. But we need to 'escape the slash', hence \\\\s (or put the regex in / / characters)\r\n// ?= is a lookahead\r\n\r\n// The RegEx has two 'Groups'\r\n// \r\n// Group 1 (finding the Key)\r\n// Look for one or more characters (a..z or A..Z)\r\n// then look for whitespace\r\n// then look for 'equals'\r\n// then look for whitespace\r\n// then look for an optional Quote character\r\n//\r\n// Group 2 (finding the Value) -\r\n// Look for EITHER 'look backwards for a Quote', some characters, 'lookahead for a Quote'\r\n// OR some characters until (by looking ahead) you can see that another key comes next. The lookahead is 'optinal whitespace' 'comma' 'optional whitespace' 'chars' 'optinal whitespace' 'equals'\r\n// OR some characters followed by 'optinal whitespace'\r\n\r\nconst WWW_AUTH = \"WWW-Authenticate\";\r\nconst WWW_AUTH_REGEX = new RegExp('([a-zA-Z]+)\\\\s*=\\\\s*\"?((?<=\").*?(?=\")|.*?(?=\\\\s*,?\\\\s*[a-zA-Z]+\\\\s*=)|.+[^\\\\s])', \"g\");\r\n\r\nenum ReadStates {\r\n SEARCHING,\r\n READING_RTSP_HEADER,\r\n READING_RTSP_PAYLOAD,\r\n READING_RAW_PACKET_SIZE,\r\n READING_RAW_PACKET,\r\n}\r\n\r\ntype Connection = \"udp\" | \"tcp\";\r\n\r\ntype AuthOptions = {\r\n type: \"Digest\" | \"Basic\",\r\n realm?: string,\r\n nonce?: string,\r\n algorithm?: \"MD5\" | \"SHA-256\",\r\n};\r\n\r\ntype Headers = {\r\n [key: string]: string | number | undefined;\r\n Session?: string;\r\n Location?: string;\r\n CSeq?: number;\r\n \"WWW-Authenticate\"?: string;\r\n Transport?: string;\r\n Unsupported?: string;\r\n};\r\n\r\n// Details for each Session within the RTSP Stream (eg video session, audio session, metadata session)\r\ntype Detail = {\r\n codec: string;\r\n mediaSource: ({ // cannot work out how to pull this type in\r\n type: string;\r\n port: number;\r\n protocol: string;\r\n payloads?: string | undefined;\r\n } & transform.MediaDescription); // get Type from the interface\r\n transport: Transport['parameters']; // get Type from the interface\r\n isH264: boolean; // legacy API\r\n rtpChannel: number;\r\n rtcpChannel: number;\r\n\r\n // Cache any optional RTCP Sender Report values (used to calculate Wall Clock Time)\r\n sr_ntpMSW?: number;\r\n sr_ntpLSW?: number;\r\n sr_rtptimestamp?: number;\r\n};\r\n\r\nexport default class RTSPClient extends EventEmitter {\r\n username: string;\r\n password: string;\r\n headers: { [key: string]: string };\r\n\r\n isConnected = false;\r\n closed = false;\r\n\r\n // These are all set in #connect or #_netConnect.\r\n\r\n _url?: string;\r\n _client?: SocketUnion;\r\n _cSeq = 0;\r\n _unsupportedExtensions?: string[];\r\n _authOpions?: AuthOptions;\r\n // Example: 'SessionId'[';timeout=seconds']\r\n _session?: string;\r\n _keepAliveID?: NodeJS.Timeout;\r\n _nextFreeInterleavedChannel = 0;\r\n _nextFreeUDPPort = 5000;\r\n\r\n readState: ReadStates = ReadStates.SEARCHING;\r\n\r\n // Used as a cache for the data stream.\r\n // What's in here is based on current #readState.\r\n messageBytes: number[] = [];\r\n\r\n // Used for parsing RTSP responses,\r\n\r\n // Content-Length header in the RTSP message.\r\n rtspContentLength = 0;\r\n rtspStatusLine = \"\";\r\n rtspHeaders: Headers = {};\r\n\r\n // Used for parsing RTP/RTCP responses.\r\n\r\n rtspPacketLength = 0;\r\n rtspPacket = Buffer.from(\"\");\r\n rtspPacketPointer = 0;\r\n\r\n // Used in #_emptyReceiverReport.\r\n clientSSRC = generateSSRC();\r\n\r\n tcpSocket: SocketUnion = new net.Socket();\r\n setupResult: Array = [];\r\n constructor(\r\n username: string,\r\n password: string,\r\n headers?: { [key: string]: string }\r\n ) {\r\n super();\r\n\r\n this.username = username;\r\n this.password = password;\r\n this.headers = {\r\n ...(headers || {}),\r\n \"User-Agent\": \"yellowstone/3.x\",\r\n };\r\n }\r\n\r\n // This manages the lifecycle for the RTSP connection\r\n // over TCP.\r\n //\r\n // Sets #_client.\r\n //\r\n // Handles receiving data & closing port, called during\r\n // #connect.\r\n _netConnect(hostname: string, port: number, secure: boolean = false): Promise {\r\n return new Promise((resolve, reject) => {\r\n // Set after listeners defined.\r\n\r\n const errorListener = (err: any) => {\r\n client.removeListener(\"error\", errorListener);\r\n reject(err);\r\n };\r\n\r\n const postConnectErrorListener = (err: any) => {\r\n client.removeListener(\"error\", postConnectErrorListener);\r\n this.emit(\"error\", err);\r\n reject(err);\r\n };\r\n\r\n const closeListener = () => {\r\n client.removeListener(\"close\", closeListener);\r\n this.emit(\"close\");\r\n this.close(true);\r\n };\r\n\r\n const responseListener = (responseName: string, headers: Headers) => {\r\n const name = responseName.split(\" \")[0];\r\n\r\n if (name.indexOf(\"RTSP/\") === 0) {\r\n return;\r\n }\r\n\r\n if (name === \"REDIRECT\" || name === \"ANNOUNCE\") {\r\n this.respond(\"200 OK\", { CSeq: headers.CSeq });\r\n }\r\n\r\n if (name === \"REDIRECT\" && headers.Location) {\r\n this.close();\r\n this.connect(headers.Location);\r\n }\r\n };\r\n\r\n // rtsp or rtsps(with tls)\r\n let client: SocketUnion;\r\n if (secure == false) {\r\n client = net.connect(port, hostname, () => {\r\n this.isConnected = true;\r\n this._client = client;\r\n\r\n client.removeListener(\"error\", errorListener);\r\n client.on(\"error\", postConnectErrorListener);\r\n\r\n this.on(\"response\", responseListener);\r\n resolve(this);\r\n });\r\n }\r\n else {\r\n const options: tls.ConnectionOptions = {\r\n rejectUnauthorized: false\r\n };\r\n client = tls.connect(port, hostname, options, () => {\r\n console.log(\"TLS Connection\");\r\n this.isConnected = true;\r\n this._client = client;\r\n\r\n client.removeListener(\"error\", errorListener);\r\n\r\n this.on(\"response\", responseListener);\r\n resolve(this);\r\n });\r\n }\r\n\r\n client.on(\"data\", this._onData.bind(this));\r\n client.on(\"error\", errorListener);\r\n client.on(\"close\", closeListener);\r\n this.tcpSocket = client;\r\n });\r\n }\r\n\r\n async connect(\r\n url: string,\r\n {\r\n keepAlive = true,\r\n connection = \"udp\",\r\n secure = false,\r\n }: { keepAlive: boolean; connection?: Connection, secure: boolean } = {\r\n keepAlive: true,\r\n connection: \"udp\",\r\n secure: false\r\n }\r\n ): Promise {\r\n const { hostname, port } = urlParse((this._url = url));\r\n if (!hostname) {\r\n throw new Error(\"URL parsing error in connect method.\");\r\n }\r\n\r\n const details: Detail[] = [];\r\n\r\n await this._netConnect(hostname, parseInt(port || \"554\"), secure);\r\n await this.request(\"OPTIONS\");\r\n\r\n const describeRes = await this.request(\"DESCRIBE\", {\r\n Accept: \"application/sdp\",\r\n });\r\n if (!describeRes || !describeRes.mediaHeaders) {\r\n throw new Error(\r\n \"No media headers on DESCRIBE; RTSP server is broken (sanity check)\"\r\n );\r\n }\r\n\r\n // For now, only RTP/AVP and RTP/AVPF are supported. (Some RTSPS servers use RTP/SAVP)\r\n const { media } = transform.parse(describeRes.mediaHeaders.join(\"\\r\\n\"));\r\n\r\n // Loop over the Media Streams in the SDP looking for Video or Audio\r\n // In theory the SDP can contain multiple Video and Audio Streams. We only want one of each type\r\n let hasVideo = false;\r\n let hasAudio = false;\r\n let hasMetaData = false;\r\n let hasBackchannel = false;\r\n\r\n for (let x = 0; x < media.length; x++) {\r\n let needSetup = false;\r\n let codec = \"\";\r\n const mediaSource = media[x];\r\n\r\n\r\n // RFC says \"If none of the direction attributes (\"sendonly\", \"recvonly\", \"inactive\", and \"sendrecv\") are present,\r\n // the \"sendrecv\" SHOULD be assumed\r\n if (mediaSource.direction == undefined) mediaSource.direction = \"sendrecv\"; // Wowza does not send 'direction'\r\n\r\n if (\r\n mediaSource.type === \"video\" &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec === \"H264\"\r\n ) {\r\n this.emit(\"log\", \"H264 Video Stream Found in SDP\", \"\");\r\n if (hasVideo == false) {\r\n needSetup = true;\r\n hasVideo = true;\r\n codec = \"H264\";\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"video\" &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec === \"H265\"\r\n ) {\r\n this.emit(\"log\", \"H265 Video Stream Found in SDP\", \"\");\r\n if (hasVideo == false) {\r\n needSetup = true;\r\n hasVideo = true;\r\n codec = \"H265\";\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"video\" &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec === \"H266\"\r\n ) {\r\n this.emit(\"log\", \"H266 Video Stream Found in SDP\", \"\");\r\n if (hasVideo == false) {\r\n needSetup = true;\r\n hasVideo = true;\r\n codec = \"H266\";\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"video\" &&\r\n (mediaSource.protocol === RTP_AVP || mediaSource.protocol === RTP_AVPF) &&\r\n mediaSource.rtp[0].codec === \"AV1\"\r\n ) {\r\n this.emit(\"log\", \"AV1 Video Stream Found in SDP\", \"\");\r\n if (hasVideo == false) {\r\n needSetup = true;\r\n hasVideo = true;\r\n codec = \"AV1\";\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"audio\" &&\r\n (mediaSource.direction === \"recvonly\" || mediaSource.direction === \"sendrecv\") &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec.toLowerCase() === \"mpeg4-generic\" && // (RFC examples are lower case. Axis cameras use upper case)\r\n mediaSource.fmtp[0].config.includes(\"AAC\")\r\n ) {\r\n this.emit(\"log\", \"AAC Audio Stream Found in SDP\", \"\");\r\n if (hasAudio == false) {\r\n needSetup = true;\r\n hasAudio = true;\r\n codec = \"AAC\";\r\n }\r\n }\r\n\r\n if (mediaSource.type === \"audio\" &&\r\n mediaSource.direction === \"sendonly\" &&\r\n mediaSource.protocol === RTP_AVP) {\r\n this.emit(\"log\", \"Audio backchannel Found in SDP\", \"\");\r\n if (hasBackchannel == false) {\r\n needSetup = true;\r\n hasBackchannel = true;\r\n codec = mediaSource.rtp[0].codec;\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"application\" &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec.toLowerCase() === \"vnd.onvif.metadata\"\r\n ) {\r\n this.emit(\"log\", \"ONVIF Meta Data Found in SDP\", \"\");\r\n if (hasMetaData == false) {\r\n needSetup = true;\r\n hasMetaData = true;\r\n codec = \"vnd.onvif.metadata\";\r\n }\r\n }\r\n\r\n if (\r\n mediaSource.type === \"application\" &&\r\n mediaSource.protocol === RTP_AVP &&\r\n mediaSource.rtp[0].codec.toUpperCase() === \"SMPTE336M\" // MediaMTX sends in capitals. Looks like the RFC suggests lower case\r\n ) {\r\n this.emit(\"log\", \"SMPTE336M KLV Data Stream Found in SDP\", \"\");\r\n if (hasMetaData == false) {\r\n needSetup = true;\r\n hasMetaData = true;\r\n codec = \"SMPTE336M\";\r\n }\r\n }\r\n\r\n if (needSetup) {\r\n let streamurl = \"\";\r\n // The 'control' in the SDP can be a relative or absolute uri\r\n if (mediaSource.control) {\r\n if (mediaSource.control.toLowerCase().startsWith(\"rtsp://\")) {\r\n // absolute path\r\n streamurl = mediaSource.control;\r\n } else {\r\n // relative path\r\n streamurl = this._url + \"/\" + mediaSource.control;\r\n }\r\n }\r\n\r\n // Perform a SETUP on the streamurl\r\n // either 'udp' RTP/RTCP packets\r\n // or with 'tcp' RTP/TCP packets which are interleaved into the TCP based RTSP socket\r\n let setupRes;\r\n let rtpChannel: number;\r\n let rtcpChannel: number;\r\n let rtpReceiver: dgram.Socket|null = null; // UDP mode init value\r\n let rtcpReceiver: dgram.Socket|null = null; // UDP mode init value\r\n\r\n if (connection === \"udp\") {\r\n // Create a pair of UDP listeners, even numbered port for RTP\r\n // and odd numbered port for RTCP\r\n\r\n rtpChannel = this._nextFreeUDPPort;\r\n rtcpChannel = this._nextFreeUDPPort + 1;\r\n this._nextFreeUDPPort += 2;\r\n\r\n const rtpPort = rtpChannel;\r\n rtpReceiver = dgram.createSocket(\"udp4\");\r\n\r\n rtpReceiver.on(\"message\", (buf, remote) => {\r\n let packet = parseRTPPacket(buf);\r\n\r\n // Add wall clock time\r\n const detail = this.setupResult.find(item => item.rtpChannel == rtpChannel);\r\n if (detail != undefined) packet.wallclockTime = this.GetWallClockTime(packet, detail);\r\n\r\n this.emit(\"data\", rtpPort, packet.payload, packet);\r\n });\r\n\r\n const rtcpPort = rtcpChannel;\r\n rtcpReceiver = dgram.createSocket(\"udp4\");\r\n\r\n rtcpReceiver.on(\"message\", (buf, remote) => {\r\n const packet = parseRTCPPacket(buf);\r\n\r\n // If this is a Sender Report, cache the NTP Wall Clock data\r\n if (packet.packetType == 200 && packet.senderReport != undefined) {\r\n let detail = this.setupResult.find(item => item.rtcpChannel == rtcpChannel);\r\n if (detail != undefined) {\r\n detail.sr_ntpMSW = packet.senderReport.ntpTimestampMSW;\r\n detail.sr_ntpLSW = packet.senderReport.ntpTimestampLSW;\r\n detail.sr_rtptimestamp = packet.senderReport.rtpTimestamp;\r\n }\r\n }\r\n\r\n this.emit(\"controlData\", rtcpPort, packet);\r\n\r\n const receiver_report = this._emptyReceiverReport();\r\n this._sendUDPData(remote.address, remote.port, receiver_report);\r\n });\r\n\r\n // Block until both UDP sockets are open.\r\n\r\n await new Promise((resolve) => {\r\n rtpReceiver?.bind(rtpPort, () => resolve({}));\r\n });\r\n\r\n await new Promise((resolve) => {\r\n rtcpReceiver?.bind(rtcpPort, () => resolve({}));\r\n });\r\n\r\n const setupHeader = {\r\n Transport: `RTP/AVP;unicast;client_port=${rtpPort}-${rtcpPort}`,\r\n };\r\n if (this._session)\r\n Object.assign(setupHeader, { Session: this._session });\r\n setupRes = await this.request(\"SETUP\", setupHeader, streamurl);\r\n } else if (connection === \"tcp\") {\r\n // channel 0, RTP\r\n // channel 1, RTCP\r\n\r\n rtpChannel = this._nextFreeInterleavedChannel;\r\n rtcpChannel = this._nextFreeInterleavedChannel + 1;\r\n this._nextFreeInterleavedChannel += 2;\r\n\r\n const setupHeader = {\r\n Transport: `RTP/AVP/TCP;interleaved=${rtpChannel}-${rtcpChannel}`,\r\n };\r\n if (this._session)\r\n Object.assign(setupHeader, { Session: this._session }); // not used on first SETUP\r\n setupRes = await this.request(\"SETUP\", setupHeader, streamurl);\r\n } else {\r\n throw new Error(\r\n `Connection parameter to RTSPClient#connect is ${connection}, not udp or tcp!`\r\n );\r\n }\r\n\r\n if (!setupRes) {\r\n throw new Error(\r\n \"No SETUP response; RTSP server is broken (sanity check)\"\r\n );\r\n }\r\n\r\n const { headers } = setupRes;\r\n\r\n if (!headers.Transport) {\r\n throw new Error(\r\n \"No Transport header on SETUP; RTSP server is broken (sanity check)\"\r\n );\r\n }\r\n\r\n const transport = parseTransport(headers.Transport);\r\n if (\r\n // TCP\r\n transport.protocol !== \"RTP/AVP/TCP\" &&\r\n // UDP\r\n transport.protocol !== \"RTP/AVP\" &&\r\n // UDP\r\n transport.protocol !== \"RTP/AVP/UDP\" // Panasonic cameras send this\r\n ) {\r\n throw new Error(\r\n \"Only RTSP servers supporting RTP/AVP or RTP/AVP/UDP or RTP/AVP/TCP are supported at this time.\"\r\n );\r\n }\r\n\r\n // Patch from zoolyka (Zoltan Hajdu).\r\n // Try to open a hole in the NAT router (to allow incoming UDP packets)\r\n // by send a UDP packet for RTP and RTCP to the remote RTSP server.\r\n // Note, Roger did not have a router that needed this so the feature is untested.\r\n // May be better to change the RTCP message to a Receiver Report, leaving the RTP message as zero bytes\r\n if (connection === \"udp\" && transport && rtpReceiver && rtcpReceiver) {\r\n rtpReceiver.send(Buffer.from(''), Number(transport.parameters[\"server_port\"].split(\"-\")[0]), hostname);\r\n rtcpReceiver.send(Buffer.from(''), Number(transport.parameters[\"server_port\"].split(\"-\")[1]), hostname);\r\n }\r\n\r\n if (headers.Unsupported) {\r\n this._unsupportedExtensions = headers.Unsupported.split(\",\");\r\n }\r\n\r\n if (headers.Session) {\r\n this._session = headers.Session.split(\";\")[0];\r\n }\r\n\r\n const detail: Detail = {\r\n codec,\r\n mediaSource,\r\n transport: transport.parameters,\r\n isH264: codec === \"H264\", // legacy API\r\n rtpChannel,\r\n rtcpChannel,\r\n };\r\n\r\n details.push(detail);\r\n } // end if (needSetup)\r\n } // end for loop, looping over each media stream\r\n\r\n if (keepAlive) {\r\n // Start a Timer to send OPTIONS every 20 seconds to keep stream alive\r\n // using the Session ID\r\n this._keepAliveID = setInterval(() => {\r\n this.request(\"OPTIONS\", { Session: this._session });\r\n // this.request(\"OPTIONS\");\r\n }, 20 * 1000);\r\n }\r\n\r\n this.setupResult = details;\r\n return details;\r\n }\r\n\r\n request(\r\n requestName: string,\r\n headersParam: Headers = {},\r\n url?: string\r\n ): Promise<{ headers: Headers; mediaHeaders?: string[] } | void> {\r\n if (!this._client) {\r\n return Promise.resolve();\r\n }\r\n\r\n if (!url) {\r\n url = this._url;\r\n }\r\n\r\n const id = ++this._cSeq;\r\n // mutable via string addition\r\n let req = `${requestName} ${url} RTSP/1.0\\r\\nCSeq: ${id}\\r\\n`;\r\n\r\n const headers = {\r\n ...this.headers,\r\n ...headersParam,\r\n };\r\n\r\n if (this._authOpions) {\r\n Object.assign(headers, {\r\n Authorization: this._generateAuthString(requestName, url),\r\n });\r\n }\r\n\r\n // NOTE:\r\n // If we cache the Authenitcation Type (Direct or Basic) then we could\r\n // re-compute an Authorization Header here and include in the RTSP Command\r\n // This would make connections a faster with fewer round-trips to the RTSP Server\r\n\r\n req += Object.entries(headers)\r\n .map(([key, value]) => `${key}: ${value}\\r\\n`)\r\n .join(\"\");\r\n\r\n this.emit(\"log\", req, \"C->S\");\r\n // Make sure to add an empty line after the request.\r\n this._client.write(`${req}\\r\\n`);\r\n\r\n return new Promise((resolve, reject) => {\r\n const responseHandler = (\r\n responseName: string,\r\n resHeaders: Headers,\r\n mediaHeaders: string[]\r\n ) => {\r\n const firstAnswer: string = String(resHeaders[\"\"]) || \"\";\r\n if (firstAnswer.indexOf(\"401\") >= 0 && 'Authorization' in headers) {\r\n // If the RTSP Command we sent included an Authorization and we have 401 error, then reject()\r\n reject(new Error(`Bad RTSP credentials!`));\r\n return;\r\n }\r\n if (resHeaders.CSeq !== id) {\r\n return;\r\n }\r\n\r\n this.removeListener(\"response\", responseHandler);\r\n\r\n const statusCode = parseInt(responseName.split(\" \")[1]);\r\n\r\n if (statusCode === STATUS_OK) {\r\n if (mediaHeaders.length > 0) {\r\n resolve({\r\n headers: resHeaders,\r\n mediaHeaders,\r\n });\r\n } else {\r\n resolve({\r\n headers: resHeaders,\r\n });\r\n }\r\n } else {\r\n const authHeader = resHeaders[WWW_AUTH];\r\n\r\n // We have status code unauthenticated.\r\n if (statusCode === STATUS_UNAUTH && authHeader) {\r\n this._authOpions = {\r\n type: authHeader.split(\" \")[0] as AuthOptions[\"type\"],\r\n algorithm: \"MD5\", // Default to MD5 if no algorthm is given. Milestone's RTSP server also supports SHA-256 for FIPS\r\n }\r\n\r\n // Get auth properties from WWW_AUTH header.\r\n let match = WWW_AUTH_REGEX.exec(authHeader);\r\n while (match != null) {\r\n const prop = match[1];\r\n\r\n if (prop == \"realm\" && match[2]) {\r\n this._authOpions.realm = match[2];\r\n }\r\n\r\n if (prop == \"nonce\" && match[2]) {\r\n this._authOpions.nonce = match[2];\r\n }\r\n\r\n if (prop == \"algorithm\" && match[2]) {\r\n this._authOpions.algorithm = match[2] as AuthOptions[\"algorithm\"];\r\n }\r\n\r\n match = WWW_AUTH_REGEX.exec(authHeader);\r\n }\r\n\r\n // Repeat the request, now _authOptions will be detected and the Authorization header will be generated\r\n resolve(this.request(requestName, headers, url));\r\n return;\r\n }\r\n\r\n reject(new Error(`Bad RTSP status code ${statusCode}!`));\r\n return;\r\n }\r\n };\r\n\r\n this.on(\"response\", responseHandler);\r\n });\r\n }\r\n\r\n respond(status: string, headersParam: Headers = {}): void {\r\n if (!this._client) {\r\n return;\r\n }\r\n\r\n // mutable via string addition\r\n let res = `RTSP/1.0 ${status}\\r\\n`;\r\n\r\n const headers = {\r\n ...this.headers,\r\n ...headersParam,\r\n };\r\n\r\n res += Object.entries(headers)\r\n .map(([key, value]) => `${key}: ${value}\\r\\n`)\r\n .join(\"\");\r\n\r\n this.emit(\"log\", res, \"C->S\");\r\n this._client.write(`${res}\\r\\n`);\r\n }\r\n\r\n async play(): Promise {\r\n if (!this.isConnected) {\r\n throw new Error(\"Client is not connected.\");\r\n }\r\n\r\n await this.request(\"PLAY\", { Session: this._session });\r\n }\r\n\r\n async pause(): Promise {\r\n if (!this.isConnected) {\r\n throw new Error(\"Client is not connected.\");\r\n }\r\n\r\n await this.request(\"PAUSE\", { Session: this._session });\r\n }\r\n\r\n async sendAudioBackChannel(audioChunk: Buffer): Promise {\r\n let rtp, buf;\r\n const bufSize = 160;\r\n while (audioChunk.length > 0) {\r\n if (audioChunk.length > bufSize) {\r\n buf = audioChunk.slice(0, bufSize);\r\n audioChunk = audioChunk.slice(bufSize, audioChunk.length);\r\n } else {\r\n buf = audioChunk.slice(0, audioChunk.length);\r\n audioChunk = Buffer.from([]);\r\n }\r\n if (!rtp)\r\n rtp = new RTPPacket(buf);\r\n else\r\n rtp.payload = buf;\r\n // rtp.type = 8;// set động\r\n rtp.time += buf.length;\r\n rtp.seq++;\r\n const bufferLength = Buffer.alloc(2);\r\n bufferLength.writeUInt16BE(rtp.packet.length, 0);\r\n let channelInterleaved = this.setupResult.filter((value) => {\r\n return value.mediaSource.type === 'audio' && value.mediaSource.direction === 'sendonly';\r\n })[0].transport.interleaved;\r\n /* RTSP Interleaved Frame structure\r\n |dollar sign|channel identifier|data length|\r\n |1 Byte |1 Byte |2 Bytes |\r\n */\r\n channelInterleaved = channelInterleaved.split('-')[0];\r\n let interleavedHeader = Buffer.from([0x24]);// set '$'\r\n interleavedHeader = Buffer.concat([interleavedHeader, Buffer.from([channelInterleaved])]);\r\n interleavedHeader = Buffer.concat([interleavedHeader, bufferLength]);\r\n const dataToSend = Buffer.concat([interleavedHeader, rtp.packet]);\r\n await this._socketWrite(this.tcpSocket, dataToSend);\r\n }\r\n return;\r\n }\r\n\r\n async close(isImmediate = false): Promise {\r\n if (this.closed) return;\r\n this.closed = true;\r\n\r\n if (!this._client) {\r\n return;\r\n }\r\n\r\n if (!isImmediate) {\r\n await this.request(\"TEARDOWN\", {\r\n Session: this._session,\r\n });\r\n }\r\n\r\n this._client.end();\r\n this.removeAllListeners(\"response\");\r\n\r\n if (this._keepAliveID != undefined) {\r\n clearInterval(this._keepAliveID);\r\n this._keepAliveID = undefined;\r\n }\r\n\r\n this.isConnected = false;\r\n this._cSeq = 0;\r\n }\r\n\r\n _onData(data: Buffer): void {\r\n let index = 0;\r\n\r\n // $\r\n const PACKET_START = 0x24;\r\n // R\r\n const RTSP_HEADER_START = 0x52;\r\n // /n\r\n const ENDL = 10;\r\n\r\n while (index < data.length) {\r\n // read RTP or RTCP packet\r\n if (\r\n this.readState == ReadStates.SEARCHING &&\r\n data[index] == PACKET_START\r\n ) {\r\n this.messageBytes = [data[index]];\r\n index++;\r\n\r\n this.readState = ReadStates.READING_RAW_PACKET_SIZE;\r\n } else if (this.readState == ReadStates.READING_RAW_PACKET_SIZE) {\r\n // accumulate bytes for $, channel and length\r\n this.messageBytes.push(data[index]);\r\n index++;\r\n\r\n if (this.messageBytes.length == 4) {\r\n this.rtspPacketLength =\r\n (this.messageBytes[2] << 8) + this.messageBytes[3];\r\n\r\n if (this.rtspPacketLength > 0) {\r\n this.rtspPacket = Buffer.alloc(this.rtspPacketLength);\r\n this.rtspPacketPointer = 0;\r\n this.readState = ReadStates.READING_RAW_PACKET;\r\n } else {\r\n this.readState = ReadStates.SEARCHING;\r\n }\r\n }\r\n } else if (this.readState == ReadStates.READING_RAW_PACKET) {\r\n this.rtspPacket[this.rtspPacketPointer++] = data[index];\r\n index++;\r\n\r\n if (this.rtspPacketPointer == this.rtspPacketLength) {\r\n const packetChannel = this.messageBytes[1];\r\n if ((packetChannel & 0x01) === 0) {\r\n // even number\r\n let packet = parseRTPPacket(this.rtspPacket);\r\n\r\n // Get the Session Detail\r\n const detail = this.setupResult.find(item => item.rtpChannel == packetChannel);\r\n if (detail != undefined) packet.wallclockTime = this.GetWallClockTime(packet, detail);\r\n\r\n this.emit(\"data\", packetChannel, packet.payload, packet);\r\n }\r\n if ((packetChannel & 0x01) === 1) {\r\n // odd number\r\n const packet = parseRTCPPacket(this.rtspPacket);\r\n\r\n // If this is a Sender Report, cache the NTP Wall Clock data\r\n if (packet.packetType == 200 && packet.senderReport != undefined) {\r\n let detail = this.setupResult.find(item => item.rtcpChannel == packetChannel);\r\n if (detail != undefined) {\r\n detail.sr_ntpMSW = packet.senderReport.ntpTimestampMSW;\r\n detail.sr_ntpLSW = packet.senderReport.ntpTimestampLSW;\r\n detail.sr_rtptimestamp = packet.senderReport.rtpTimestamp;\r\n }\r\n }\r\n \r\n this.emit(\"controlData\", packetChannel, packet);\r\n\r\n const receiver_report = this._emptyReceiverReport();\r\n this._sendInterleavedData(packetChannel, receiver_report);\r\n }\r\n this.readState = ReadStates.SEARCHING;\r\n }\r\n // read response data\r\n } else if (\r\n this.readState == ReadStates.SEARCHING &&\r\n data[index] == RTSP_HEADER_START\r\n ) {\r\n // found the start of a RTSP rtsp_message\r\n this.messageBytes = [data[index]];\r\n index++;\r\n\r\n this.readState = ReadStates.READING_RTSP_HEADER;\r\n } else if (this.readState == ReadStates.READING_RTSP_HEADER) {\r\n // Reading a RTSP message.\r\n\r\n // Add character to the messageBytes\r\n // Ignore /r (13) but keep /n (10)\r\n if (data[index] != 13) {\r\n this.messageBytes.push(data[index]);\r\n }\r\n index++;\r\n\r\n // if we have two new lines back to back then we have a complete RTSP command,\r\n // note we may still need to read the Content Payload (the body) e.g. the SDP\r\n if (\r\n this.messageBytes.length >= 2 &&\r\n this.messageBytes[this.messageBytes.length - 2] == ENDL &&\r\n this.messageBytes[this.messageBytes.length - 1] == ENDL\r\n ) {\r\n // Parse the Header\r\n\r\n const text = String.fromCharCode.apply(null, this.messageBytes);\r\n const lines = text.split(\"\\n\");\r\n\r\n this.rtspContentLength = 0;\r\n this.rtspStatusLine = lines[0];\r\n this.rtspHeaders = {};\r\n\r\n lines.forEach((line) => {\r\n const indexOf = line.indexOf(\":\");\r\n\r\n if (indexOf !== line.length - 1) {\r\n const key = line.substring(0, indexOf).trim();\r\n const data = line.substring(indexOf + 1).trim();\r\n\r\n if (key == \"Session\") this.rtspHeaders[key] = data;\r\n\r\n else if (key == \"WWW-Authenticate\") {\r\n // Handle multiple WWW-Authenticate entries and pick the 'best'\r\n // We prefer 'Digest' over 'Basic'\r\n // We preger 'Digest SHAxxx' over 'Digest MD5' or 'Digest with no algorithm (defaults to MD5) (STILL TODO)\r\n if (key in this.rtspHeaders) {\r\n console.log(\"Duplicate WWW-Authenticate keys\")\r\n if (data.startsWith(\"Digest\") && this.rtspHeaders[key]?.startsWith(\"Basic\")) {\r\n this.rtspHeaders[key] = data; // Replace Basic with Digest\r\n }\r\n console.log(\"Keeping WWW-Authenticate: \" + this.rtspHeaders[key]);\r\n } else {\r\n this.rtspHeaders[key] = data;\r\n }\r\n }\r\n\r\n else {\r\n // Store the result as either a String type or a Number type\r\n this.rtspHeaders[key] =\r\n data.match(/^[0-9]+$/)\r\n ? parseInt(data, 10)\r\n : data;\r\n }\r\n\r\n // workaround for buggy Hipcam RealServer/V1.0 camera which returns Content-length and not Content-Length\r\n if (key.toLowerCase() == \"content-length\") {\r\n this.rtspContentLength = parseInt(data, 10);\r\n }\r\n }\r\n });\r\n\r\n // if no content length, there there's no media headers\r\n // emit the message\r\n if (!this.rtspContentLength) {\r\n this.emit(\"log\", text, \"S->C\");\r\n\r\n this.emit(\"response\", this.rtspStatusLine, this.rtspHeaders, []);\r\n this.readState = ReadStates.SEARCHING;\r\n } else {\r\n this.messageBytes = [];\r\n this.readState = ReadStates.READING_RTSP_PAYLOAD;\r\n }\r\n }\r\n } else if (\r\n this.readState == ReadStates.READING_RTSP_PAYLOAD &&\r\n this.messageBytes.length < this.rtspContentLength\r\n ) {\r\n // Copy data into the RTSP payload\r\n this.messageBytes.push(data[index]);\r\n index++;\r\n\r\n if (this.messageBytes.length == this.rtspContentLength) {\r\n const text = String.fromCharCode.apply(null, this.messageBytes);\r\n const mediaHeaders = text.split(\"\\n\");\r\n\r\n // Emit the RTSP message\r\n this.emit(\r\n \"log\",\r\n String.fromCharCode.apply(null, this.messageBytes) + text,\r\n \"S->C\"\r\n );\r\n\r\n this.emit(\r\n \"response\",\r\n this.rtspStatusLine,\r\n this.rtspHeaders,\r\n mediaHeaders\r\n );\r\n this.readState = ReadStates.SEARCHING;\r\n }\r\n } else {\r\n // unexpected data\r\n throw new Error(\r\n \"Bug in RTSP data framing, please file an issue with the author with stacktrace.\"\r\n );\r\n }\r\n } // end while\r\n }\r\n\r\n _sendInterleavedData(channel: number, buffer: Buffer): void {\r\n if (!this._client) {\r\n return;\r\n }\r\n\r\n const req = `${buffer.length} bytes of interleaved data on channel ${channel}`;\r\n this.emit(\"log\", req, \"C->S\");\r\n\r\n const header = Buffer.alloc(4);\r\n header[0] = 0x24; // ascii $\r\n header[1] = channel;\r\n header[2] = (buffer.length >> 8) & 0xff;\r\n header[3] = (buffer.length >> 0) & 0xff;\r\n\r\n const data = Buffer.concat([header, buffer]);\r\n this._client.write(data);\r\n }\r\n\r\n _sendUDPData(host: string, port: number, buffer: Buffer): void {\r\n const udp = dgram.createSocket(\"udp4\");\r\n udp.send(buffer, 0, buffer.length, port, host, (err, bytes) => {\r\n // TODO: Don't ignore errors.\r\n udp.close();\r\n });\r\n }\r\n\r\n _emptyReceiverReport(): Buffer {\r\n const report = Buffer.alloc(8);\r\n const version = 2;\r\n const paddingBit = 0;\r\n const reportCount = 0; // an empty report\r\n const packetType = 201; // Receiver Report\r\n const length = report.length / 4 - 1; // num 32 bit words minus 1\r\n report[0] = (version << 6) + (paddingBit << 5) + reportCount;\r\n report[1] = packetType;\r\n report[2] = (length >> 8) & 0xff;\r\n report[3] = (length >> 0) & 0xff;\r\n report[4] = (this.clientSSRC >> 24) & 0xff;\r\n report[5] = (this.clientSSRC >> 16) & 0xff;\r\n report[6] = (this.clientSSRC >> 8) & 0xff;\r\n report[7] = (this.clientSSRC >> 0) & 0xff;\r\n\r\n return report;\r\n }\r\n\r\n async _socketWrite(socket: SocketUnion, data: Buffer): Promise {\r\n return new Promise((resolve, reject) => {\r\n setTimeout(() => {\r\n socket.write(data, (error: any) => {\r\n if (error) {\r\n reject(error);\r\n } else {\r\n resolve(undefined);\r\n }\r\n })\r\n }, 20);\r\n })\r\n }\r\n\r\n private _generateAuthString(requestName: string, url?: string): string {\r\n\r\n if (!url) {\r\n url = this._url;\r\n }\r\n\r\n let authString = \"\";\r\n if (!this._authOpions) return \"\";\r\n if (this._authOpions.type === \"Digest\") {\r\n // Digest Authentication\r\n\r\n // Select Hash Function, default to MD5\r\n const HashFunction = (this._authOpions.algorithm == \"SHA-256\" ? getSHA256Hash : getMD5Hash);\r\n\r\n const ha1 = HashFunction(\r\n `${this.username}:${this._authOpions.realm}:${this.password}`\r\n );\r\n const ha2 = HashFunction(`${requestName}:${url}`);\r\n const ha3 = HashFunction(`${ha1}:${this._authOpions.nonce}:${ha2}`);\r\n\r\n // Some RTSP servers to not accept \"algorithm=NNN\" in the authString and reject the authentication. So only add algorithm=ZZZZ when not using MD5\r\n if (this._authOpions.algorithm == \"MD5\")\r\n authString = `Digest username=\"${this.username}\",realm=\"${this._authOpions.realm}\",nonce=\"${this._authOpions.nonce}\",uri=\"${url}\",response=\"${ha3}\"`;\r\n else\r\n authString = `Digest username=\"${this.username}\",realm=\"${this._authOpions.realm}\",nonce=\"${this._authOpions.nonce}\",algorithm=${this._authOpions.algorithm},uri=\"${url}\",response=\"${ha3}\"`;\r\n } else if (this._authOpions.type === \"Basic\") {\r\n // Basic Authentication\r\n // https://xkcd.com/538/\r\n const b64 = Buffer.from(\r\n `${this.username}:${this.password}`\r\n ).toString(\"base64\");\r\n authString = `Basic ${b64}`;\r\n }\r\n return authString;\r\n }\r\n\r\n ntpBaseDate_ms = new Date(\"1900/1/1\").getTime();\r\n\r\n // Note we have had a RTP Packet in Yellowstone for many years, but the Audio Backchennal code added another object also called RTPPacket\r\n GetWallClockTime(packet: util.RTPPacket, detail: Detail): Date | undefined {\r\n\r\n // Add Wall Clock Time\r\n if (detail.sr_ntpMSW != undefined && detail.sr_ntpLSW != undefined && detail.sr_rtptimestamp != undefined && detail.mediaSource.rtp[0].rate != undefined) {\r\n let refTimestampSecs = detail.sr_rtptimestamp / detail.mediaSource.rtp[0].rate; // H264 is 90 kHz clock rate\r\n let packetTimestampSecs = packet.timestamp / detail.mediaSource.rtp[0].rate; // eg 90kHz\r\n let packetTimestampDeltaSecs = packetTimestampSecs - refTimestampSecs;\r\n let refTimestamp = new Date(this.ntpBaseDate_ms + (detail.sr_ntpMSW * 1000) + ((detail.sr_ntpLSW/Math.pow(2,32))*1000));\r\n let wallclockTime = new Date(refTimestamp.getTime() + (packetTimestampDeltaSecs*1000));\r\n return wallclockTime;\r\n }\r\n\r\n // Could not generate a Wall Clock Time\r\n return undefined;\r\n }\r\n\r\n}\r\n\r\nexport { RTPPacket, RTCPPacket } from \"./util\";\r\n"]} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts index 9a18fb1..f1ad506 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -3,8 +3,9 @@ import H265Transport from "./transports/H265Transport"; import H266Transport from "./transports/H266Transport"; import AV1Transport from "./transports/AV1Transport"; import AACTransport from "./transports/AACTransport"; +import SMPTE336MKLVTransport from "./transports/SMPTE336MKLVTransport"; import ONVIFMetadataTransport from "./transports/ONVIFMetadataTransport"; import ONVIFClient from "./ONVIFClient"; import RTSPClient from "./RTSPClient"; import { RTPPacket, RTCPPacket } from "./util"; -export { H264Transport, H265Transport, H266Transport, AV1Transport, AACTransport, ONVIFMetadataTransport, ONVIFClient, RTSPClient, RTPPacket, RTCPPacket }; +export { H264Transport, H265Transport, H266Transport, AV1Transport, AACTransport, SMPTE336MKLVTransport, ONVIFMetadataTransport, ONVIFClient, RTSPClient, RTPPacket, RTCPPacket }; diff --git a/dist/index.js b/dist/index.js index d365638..602e696 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.RTSPClient = exports.ONVIFClient = exports.ONVIFMetadataTransport = exports.AACTransport = exports.AV1Transport = exports.H266Transport = exports.H265Transport = exports.H264Transport = void 0; +exports.RTSPClient = exports.ONVIFClient = exports.ONVIFMetadataTransport = exports.SMPTE336MKLVTransport = exports.AACTransport = exports.AV1Transport = exports.H266Transport = exports.H265Transport = exports.H264Transport = void 0; const H264Transport_1 = require("./transports/H264Transport"); exports.H264Transport = H264Transport_1.default; const H265Transport_1 = require("./transports/H265Transport"); @@ -11,6 +11,8 @@ const AV1Transport_1 = require("./transports/AV1Transport"); exports.AV1Transport = AV1Transport_1.default; const AACTransport_1 = require("./transports/AACTransport"); exports.AACTransport = AACTransport_1.default; +const SMPTE336MKLVTransport_1 = require("./transports/SMPTE336MKLVTransport"); +exports.SMPTE336MKLVTransport = SMPTE336MKLVTransport_1.default; const ONVIFMetadataTransport_1 = require("./transports/ONVIFMetadataTransport"); exports.ONVIFMetadataTransport = ONVIFMetadataTransport_1.default; const ONVIFClient_1 = require("./ONVIFClient"); diff --git a/dist/index.js.map b/dist/index.js.map index 0d3df35..479fa8b 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;AAAA,8DAAuD;AAWrD,wBAXK,uBAAa,CAWL;AAVf,8DAAuD;AAWrD,wBAXK,uBAAa,CAWL;AAVf,8DAAuD;AAWrD,wBAXK,uBAAa,CAWL;AAVf,4DAAqD;AAWnD,uBAXK,sBAAY,CAWL;AAVd,4DAAqD;AAWnD,uBAXK,sBAAY,CAWL;AAVd,gFAAyE;AAWvE,iCAXK,gCAAsB,CAWL;AAVxB,+CAAwC;AAWtC,sBAXK,qBAAW,CAWL;AAVb,6CAAsC;AAWpC,qBAXK,oBAAU,CAWL","sourcesContent":["import H264Transport from \"./transports/H264Transport\";\r\nimport H265Transport from \"./transports/H265Transport\";\r\nimport H266Transport from \"./transports/H266Transport\";\r\nimport AV1Transport from \"./transports/AV1Transport\";\r\nimport AACTransport from \"./transports/AACTransport\";\r\nimport ONVIFMetadataTransport from \"./transports/ONVIFMetadataTransport\";\r\nimport ONVIFClient from \"./ONVIFClient\";\r\nimport RTSPClient from \"./RTSPClient\";\r\nimport {RTPPacket, RTCPPacket} from \"./util\";\r\n\r\nexport {\r\n H264Transport,\r\n H265Transport,\r\n H266Transport,\r\n AV1Transport,\r\n AACTransport,\r\n ONVIFMetadataTransport,\r\n ONVIFClient,\r\n RTSPClient,\r\n RTPPacket,\r\n RTCPPacket\r\n}\r\n"]} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;AAAA,8DAAuD;AAYrD,wBAZK,uBAAa,CAYL;AAXf,8DAAuD;AAYrD,wBAZK,uBAAa,CAYL;AAXf,8DAAuD;AAYrD,wBAZK,uBAAa,CAYL;AAXf,4DAAqD;AAYnD,uBAZK,sBAAY,CAYL;AAXd,4DAAqD;AAYnD,uBAZK,sBAAY,CAYL;AAXd,8EAAuE;AAYrE,gCAZK,+BAAqB,CAYL;AAXvB,gFAAyE;AAYvE,iCAZK,gCAAsB,CAYL;AAXxB,+CAAwC;AAYtC,sBAZK,qBAAW,CAYL;AAXb,6CAAsC;AAYpC,qBAZK,oBAAU,CAYL","sourcesContent":["import H264Transport from \"./transports/H264Transport\";\r\nimport H265Transport from \"./transports/H265Transport\";\r\nimport H266Transport from \"./transports/H266Transport\";\r\nimport AV1Transport from \"./transports/AV1Transport\";\r\nimport AACTransport from \"./transports/AACTransport\";\r\nimport SMPTE336MKLVTransport from \"./transports/SMPTE336MKLVTransport\";\r\nimport ONVIFMetadataTransport from \"./transports/ONVIFMetadataTransport\";\r\nimport ONVIFClient from \"./ONVIFClient\";\r\nimport RTSPClient from \"./RTSPClient\";\r\nimport {RTPPacket, RTCPPacket} from \"./util\";\r\n\r\nexport {\r\n H264Transport,\r\n H265Transport,\r\n H266Transport,\r\n AV1Transport,\r\n AACTransport,\r\n SMPTE336MKLVTransport,\r\n ONVIFMetadataTransport,\r\n ONVIFClient,\r\n RTSPClient,\r\n RTPPacket,\r\n RTCPPacket\r\n}\r\n"]} \ No newline at end of file diff --git a/dist/transports/H264Transport.js b/dist/transports/H264Transport.js index 7a9a289..1c26257 100644 --- a/dist/transports/H264Transport.js +++ b/dist/transports/H264Transport.js @@ -27,17 +27,30 @@ class H264Transport { if (!fmtp) { return; } + // Normally the SDP includes the sprop-parameter-sets with the SPS and PPS data + // However for MPEGTS converted to RTSP with MediaMTX, there is no sprop-parameter-sets const fmtpConfig = transform.parseParams(fmtp.config); - const splitSpropParameterSets = fmtpConfig['sprop-parameter-sets'].toString().split(','); - const sps_base64 = splitSpropParameterSets[0]; - const pps_base64 = splitSpropParameterSets[1]; - const sps = Buffer.from(sps_base64, "base64"); - const pps = Buffer.from(pps_base64, "base64"); - this.stream.write(H264_HEADER); - this.stream.write(sps); - this.stream.write(H264_HEADER); - this.stream.write(pps); - this._headerWritten = true; + if ('sprop-parameter-sets' in fmtpConfig) { + const splitSpropParameterSets = fmtpConfig['sprop-parameter-sets'].toString().split(','); + const sps_base64 = splitSpropParameterSets[0]; + const pps_base64 = splitSpropParameterSets[1]; + const sps = Buffer.from(sps_base64, "base64"); + const pps = Buffer.from(pps_base64, "base64"); + this.stream.write(H264_HEADER); + this.stream.write(sps); + this.stream.write(H264_HEADER); + this.stream.write(pps); + this._headerWritten = true; + } + else { + // Ideally MediaMTX would have parsed the MPEGTS stream, extracted the SPS and PPS and then + // placed it in the RTSP DESCRIBE SDP, but it does not do that. + // The correct method is to parse the RTP Payloads until we see the NAL type for SPS + // and the NAL type for PPS, then we can write them and set this._headerWritten to true + // But for now we will just set this._headerWritten to true and let NALS be written to disk + // before the first SPS and PPS data + this._headerWritten = true; // HACK - should be parsing the NALs for SPS and PPS + } } processRTPPacket(packet) { // Accumatate RTP packets diff --git a/dist/transports/H264Transport.js.map b/dist/transports/H264Transport.js.map index 066d89c..d31e825 100644 --- a/dist/transports/H264Transport.js.map +++ b/dist/transports/H264Transport.js.map @@ -1 +1 @@ -{"version":3,"file":"H264Transport.js","sourceRoot":"","sources":["../../lib/transports/H264Transport.ts"],"names":[],"mappings":";AAAA,8BAA8B;AAC9B,uDAAuD;AACvD,sCAAsC;;AAKtC,2CAA2C;AAG3C,oBAAoB;AACpB,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,CAAC,CAAC,CAAC;AASvD,MAAqB,aAAa;IAQhC,YAAY,MAAkB,EAAE,MAAgB,EAAE,OAAgB;QAJlE,eAAU,GAAa,EAAE,CAAC;QAE1B,mBAAc,GAAG,KAAK,CAAC;QAGrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;YAC1C,IAAI,OAAO,IAAI,OAAO,CAAC,UAAU,EAAE;gBACjC,IAAI,IAAI,CAAC,cAAc,EAAE;oBACvB,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;iBAC/B;aACF;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,wBAAwB,CAAC,OAAgB;QACvC,2DAA2D;QAC3D,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAE3C,IAAI,CAAC,IAAI,EAAE;YACT,OAAO;SACR;QAED,MAAM,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,uBAAuB,GAAG,UAAU,CAAC,sBAAsB,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzF,MAAM,UAAU,GAAG,uBAAuB,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,uBAAuB,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAE9C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEvB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;IAC7B,CAAC;IAED,gBAAgB,CAAC,MAAiB;QAChC,yBAAyB;QACzB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAErC,yEAAyE;QACzE,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE;YACtB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACtC,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;SACtB;IACH,CAAC;IAED,eAAe,CAAC,UAAoB;QAClC,MAAM,IAAI,GAAG,EAAE,CAAC;QAChB,IAAI,UAAU,GAAG,EAAE,CAAC;QAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAC1C,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YAC7B,MAAM,gBAAgB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;YACjD,MAAM,cAAc,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;YAC/C,MAAM,eAAe,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;YAEhD,IAAI,eAAe,IAAI,CAAC,IAAI,eAAe,IAAI,EAAE,EAAE,EAAE,6BAA6B;gBAChF,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;aACnB;iBAAM,IAAI,eAAe,IAAI,EAAE,EAAE,EAAE,2DAA2D;gBAC7F,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,iDAAiD;gBAC9D,4EAA4E;gBAC5E,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE;oBACpC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;oBACzD,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;oBACd,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAC,GAAG,GAAC,IAAI,CAAC,CAAC,CAAC;oBACtC,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;iBAClB;aACF;iBAAM,IAAI,eAAe,IAAI,EAAE,EAAE,EAAE,SAAS;gBAC3C,gBAAgB;aACjB;iBAAM,IAAI,eAAe,IAAI,EAAE,EAAE,EAAE,UAAU;gBAC5C,gBAAgB;aACjB;iBAAM,IAAI,eAAe,IAAI,EAAE,EAAE,EAAE,UAAU;gBAC5C,gBAAgB;aACjB;iBAAM,IAAI,eAAe,IAAI,EAAE,EAAE,EAAE,YAAY;gBAC9C,wCAAwC;gBACxC,uCAAuC;gBACvC,kCAAkC;gBAClC,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAE,eAAe;gBAC7D,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAE,aAAa;gBAC3D,MAAM,WAAW,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAE,wBAAwB;gBACtE,MAAM,cAAc,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,2BAA2B;gBAE3E,4BAA4B;gBAC5B,IAAI,WAAW,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,EAAE,qBAAqB;oBAC/D,MAAM,sBAAsB,GAAG,CAAC,gBAAgB,IAAI,CAAC,CAAC;0BACtB,CAAC,cAAc,IAAI,CAAC,CAAC;0BACrB,cAAc,CAAC;oBAC/C,UAAU,GAAG,EAAE,CAAC;oBAChB,UAAU,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;oBAExC,sDAAsD;oBACtD,KAAK,IAAI,CAAC,GAAC,CAAC,EAAE,CAAC,GAAE,MAAM,CAAC,MAAM,EAAC,CAAC,EAAE;wBAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;iBAChE;gBAED,IAAI,WAAW,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,EAAE,2BAA2B;oBACrE,KAAK,IAAI,CAAC,GAAC,CAAC,EAAE,CAAC,GAAE,MAAM,CAAC,MAAM,EAAC,CAAC,EAAE;wBAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;iBAChE;gBAED,IAAI,WAAW,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,EAAE,mBAAmB;oBAC7D,KAAK,IAAI,CAAC,GAAC,CAAC,EAAE,CAAC,GAAE,MAAM,CAAC,MAAM,EAAC,CAAC,EAAE;wBAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC/D,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;iBACpC;aACF;iBAAM,IAAI,eAAe,IAAI,EAAE,EAAE,EAAE,YAAY;gBAC9C,gBAAgB;aACjB;SACF;QAED,yBAAyB;QACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;SAC9B;IACH,CAAC;CACF;AA5HD,gCA4HC","sourcesContent":["// Process SDP and RTP packets\r\n// De-packetize RTP packets to re-create H264 NAL Units\r\n// Write H264 NAL units to a .264 file\r\n\r\nimport RTSPClient from \"../RTSPClient\";\r\nimport {RTPPacket} from \"../util\";\r\n\r\nimport * as transform from \"sdp-transform\";\r\nimport { Writable } from \"stream\";\r\n\r\n// .h264 file header\r\nconst H264_HEADER = Buffer.from([0x00,0x00,0x00,0x01]);\r\n\r\ninterface Details {\r\n codec: string\r\n mediaSource: transform.MediaDescription\r\n rtpChannel: number,\r\n rtcpChannel: number\r\n}\r\n\r\nexport default class H264Transport {\r\n client: RTSPClient;\r\n stream: Writable;\r\n\r\n rtpPackets: Buffer[] = [];\r\n\r\n _headerWritten = false;\r\n\r\n constructor(client: RTSPClient, stream: Writable, details: Details) {\r\n this.client = client;\r\n this.stream = stream;\r\n\r\n client.on(\"data\", (channel, data, packet) => {\r\n if (channel == details.rtpChannel) {\r\n if (this._headerWritten) {\r\n this.processRTPPacket(packet);\r\n }\r\n }\r\n });\r\n\r\n this.processConnectionDetails(details);\r\n }\r\n\r\n processConnectionDetails(details: Details): void {\r\n // Extract SPS and PPS from the MediaSource part of the SDP\r\n const fmtp = (details.mediaSource.fmtp)[0];\r\n \r\n if (!fmtp) {\r\n return;\r\n }\r\n \r\n const fmtpConfig = transform.parseParams(fmtp.config);\r\n const splitSpropParameterSets = fmtpConfig['sprop-parameter-sets'].toString().split(',');\r\n const sps_base64 = splitSpropParameterSets[0];\r\n const pps_base64 = splitSpropParameterSets[1];\r\n const sps = Buffer.from(sps_base64, \"base64\");\r\n const pps = Buffer.from(pps_base64, \"base64\");\r\n\r\n this.stream.write(H264_HEADER);\r\n this.stream.write(sps);\r\n this.stream.write(H264_HEADER);\r\n this.stream.write(pps);\r\n\r\n this._headerWritten = true;\r\n }\r\n\r\n processRTPPacket(packet: RTPPacket): void {\r\n // Accumatate RTP packets\r\n this.rtpPackets.push(packet.payload);\r\n \r\n // When Marker is set to 1 pass the group of packets to processRTPFrame()\r\n if (packet.marker == 1) {\r\n this.processRTPFrame(this.rtpPackets);\r\n this.rtpPackets = [];\r\n }\r\n }\r\n\r\n processRTPFrame(rtpPackets: Buffer[]): void {\r\n const nals = [];\r\n let partialNal = [];\r\n\r\n for (let i = 0; i < rtpPackets.length; i++) {\r\n const packet = rtpPackets[i];\r\n const nal_header_f_bit = (packet[0] >> 7) & 0x01;\r\n const nal_header_nri = (packet[0] >> 5) & 0x03;\r\n const nal_header_type = (packet[0] >> 0) & 0x1F;\r\n\r\n if (nal_header_type >= 1 && nal_header_type <= 23) { // Normal NAL. Not fragmented\r\n nals.push(packet);\r\n } else if (nal_header_type == 24) { // Aggregation type STAP-A. Multiple NAls in one RTP Packet\r\n let ptr = 1; // start after the nal_header_type which was '24'\r\n // if we have at least 2 more bytes (the 16 bit size) then consume more data\r\n while (ptr + 2 < (packet.length - 1)) {\r\n const size = (packet[ptr] << 8) + (packet[ptr + 1] << 0);\r\n ptr = ptr + 2;\r\n nals.push(packet.slice(ptr,ptr+size));\r\n ptr = ptr + size;\r\n }\r\n } else if (nal_header_type == 25) { // STAP-B\r\n // Not supported\r\n } else if (nal_header_type == 26) { // MTAP-16\r\n // Not supported\r\n } else if (nal_header_type == 27) { // MTAP-24\r\n // Not supported\r\n } else if (nal_header_type == 28) { // Frag FU-A\r\n // NAL is split over several RTP packets\r\n // Accumulate them in a tempoary buffer\r\n // Parse Fragmentation Unit Header\r\n const fu_header_s = (packet[1] >> 7) & 0x01; // start marker\r\n const fu_header_e = (packet[1] >> 6) & 0x01; // end marker\r\n const fu_header_r = (packet[1] >> 5) & 0x01; // reserved. should be 0\r\n const fu_header_type = (packet[1] >> 0) & 0x1F; // Original NAL unit header\r\n\r\n // Check Start and End flags\r\n if (fu_header_s == 1 && fu_header_e == 0) { // Start of Fragment}\r\n const reconstructed_nal_type = (nal_header_f_bit << 7)\r\n + (nal_header_nri << 5)\r\n + fu_header_type;\r\n partialNal = [];\r\n partialNal.push(reconstructed_nal_type);\r\n\r\n // copy the rest of the RTP payload to the temp buffer\r\n for (let x=2; x< packet.length;x++) partialNal.push(packet[x]);\r\n }\r\n\r\n if (fu_header_s == 0 && fu_header_e == 0) { // Middle part of fragment}\r\n for (let x=2; x< packet.length;x++) partialNal.push(packet[x]);\r\n }\r\n\r\n if (fu_header_s == 0 && fu_header_e == 1) { // End of fragment}\r\n for (let x=2; x< packet.length;x++) partialNal.push(packet[x]);\r\n nals.push(Buffer.from(partialNal));\r\n }\r\n } else if (nal_header_type == 29) { // Frag FU-B\r\n // Not supported\r\n }\r\n }\r\n\r\n // Write out all the NALs\r\n for (let x = 0; x < nals.length; x++) {\r\n this.stream.write(H264_HEADER);\r\n this.stream.write(nals[x]);\r\n }\r\n }\r\n}\r\n"]} \ No newline at end of file +{"version":3,"file":"H264Transport.js","sourceRoot":"","sources":["../../lib/transports/H264Transport.ts"],"names":[],"mappings":"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","sourcesContent":["// Process SDP and RTP packets\r\n// De-packetize RTP packets to re-create H264 NAL Units\r\n// Write H264 NAL units to a .264 file\r\n\r\nimport RTSPClient from \"../RTSPClient\";\r\nimport {RTPPacket} from \"../util\";\r\n\r\nimport * as transform from \"sdp-transform\";\r\nimport { Writable } from \"stream\";\r\n\r\n// .h264 file header\r\nconst H264_HEADER = Buffer.from([0x00,0x00,0x00,0x01]);\r\n\r\ninterface Details {\r\n codec: string\r\n mediaSource: transform.MediaDescription\r\n rtpChannel: number,\r\n rtcpChannel: number\r\n}\r\n\r\nexport default class H264Transport {\r\n client: RTSPClient;\r\n stream: Writable;\r\n\r\n rtpPackets: Buffer[] = [];\r\n\r\n _headerWritten = false;\r\n\r\n constructor(client: RTSPClient, stream: Writable, details: Details) {\r\n this.client = client;\r\n this.stream = stream;\r\n\r\n client.on(\"data\", (channel, data, packet) => {\r\n if (channel == details.rtpChannel) {\r\n if (this._headerWritten) {\r\n this.processRTPPacket(packet);\r\n }\r\n }\r\n });\r\n\r\n this.processConnectionDetails(details);\r\n }\r\n\r\n processConnectionDetails(details: Details): void {\r\n // Extract SPS and PPS from the MediaSource part of the SDP\r\n const fmtp = (details.mediaSource.fmtp)[0];\r\n \r\n if (!fmtp) {\r\n return;\r\n }\r\n \r\n // Normally the SDP includes the sprop-parameter-sets with the SPS and PPS data\r\n // However for MPEGTS converted to RTSP with MediaMTX, there is no sprop-parameter-sets\r\n const fmtpConfig = transform.parseParams(fmtp.config);\r\n if ('sprop-parameter-sets' in fmtpConfig) {\r\n const splitSpropParameterSets = fmtpConfig['sprop-parameter-sets'].toString().split(',');\r\n const sps_base64 = splitSpropParameterSets[0];\r\n const pps_base64 = splitSpropParameterSets[1];\r\n const sps = Buffer.from(sps_base64, \"base64\");\r\n const pps = Buffer.from(pps_base64, \"base64\");\r\n\r\n this.stream.write(H264_HEADER);\r\n this.stream.write(sps);\r\n this.stream.write(H264_HEADER);\r\n this.stream.write(pps);\r\n\r\n this._headerWritten = true;\r\n }\r\n else\r\n {\r\n // Ideally MediaMTX would have parsed the MPEGTS stream, extracted the SPS and PPS and then\r\n // placed it in the RTSP DESCRIBE SDP, but it does not do that.\r\n // The correct method is to parse the RTP Payloads until we see the NAL type for SPS\r\n // and the NAL type for PPS, then we can write them and set this._headerWritten to true\r\n\r\n // But for now we will just set this._headerWritten to true and let NALS be written to disk\r\n // before the first SPS and PPS data\r\n this._headerWritten = true; // HACK - should be parsing the NALs for SPS and PPS\r\n }\r\n\r\n }\r\n\r\n processRTPPacket(packet: RTPPacket): void {\r\n // Accumatate RTP packets\r\n this.rtpPackets.push(packet.payload);\r\n \r\n // When Marker is set to 1 pass the group of packets to processRTPFrame()\r\n if (packet.marker == 1) {\r\n this.processRTPFrame(this.rtpPackets);\r\n this.rtpPackets = [];\r\n }\r\n }\r\n\r\n processRTPFrame(rtpPackets: Buffer[]): void {\r\n const nals = [];\r\n let partialNal = [];\r\n\r\n for (let i = 0; i < rtpPackets.length; i++) {\r\n const packet = rtpPackets[i];\r\n const nal_header_f_bit = (packet[0] >> 7) & 0x01;\r\n const nal_header_nri = (packet[0] >> 5) & 0x03;\r\n const nal_header_type = (packet[0] >> 0) & 0x1F;\r\n\r\n if (nal_header_type >= 1 && nal_header_type <= 23) { // Normal NAL. Not fragmented\r\n nals.push(packet);\r\n } else if (nal_header_type == 24) { // Aggregation type STAP-A. Multiple NAls in one RTP Packet\r\n let ptr = 1; // start after the nal_header_type which was '24'\r\n // if we have at least 2 more bytes (the 16 bit size) then consume more data\r\n while (ptr + 2 < (packet.length - 1)) {\r\n const size = (packet[ptr] << 8) + (packet[ptr + 1] << 0);\r\n ptr = ptr + 2;\r\n nals.push(packet.slice(ptr,ptr+size));\r\n ptr = ptr + size;\r\n }\r\n } else if (nal_header_type == 25) { // STAP-B\r\n // Not supported\r\n } else if (nal_header_type == 26) { // MTAP-16\r\n // Not supported\r\n } else if (nal_header_type == 27) { // MTAP-24\r\n // Not supported\r\n } else if (nal_header_type == 28) { // Frag FU-A\r\n // NAL is split over several RTP packets\r\n // Accumulate them in a tempoary buffer\r\n // Parse Fragmentation Unit Header\r\n const fu_header_s = (packet[1] >> 7) & 0x01; // start marker\r\n const fu_header_e = (packet[1] >> 6) & 0x01; // end marker\r\n const fu_header_r = (packet[1] >> 5) & 0x01; // reserved. should be 0\r\n const fu_header_type = (packet[1] >> 0) & 0x1F; // Original NAL unit header\r\n\r\n // Check Start and End flags\r\n if (fu_header_s == 1 && fu_header_e == 0) { // Start of Fragment}\r\n const reconstructed_nal_type = (nal_header_f_bit << 7)\r\n + (nal_header_nri << 5)\r\n + fu_header_type;\r\n partialNal = [];\r\n partialNal.push(reconstructed_nal_type);\r\n\r\n // copy the rest of the RTP payload to the temp buffer\r\n for (let x=2; x< packet.length;x++) partialNal.push(packet[x]);\r\n }\r\n\r\n if (fu_header_s == 0 && fu_header_e == 0) { // Middle part of fragment}\r\n for (let x=2; x< packet.length;x++) partialNal.push(packet[x]);\r\n }\r\n\r\n if (fu_header_s == 0 && fu_header_e == 1) { // End of fragment}\r\n for (let x=2; x< packet.length;x++) partialNal.push(packet[x]);\r\n nals.push(Buffer.from(partialNal));\r\n }\r\n } else if (nal_header_type == 29) { // Frag FU-B\r\n // Not supported\r\n }\r\n }\r\n\r\n // Write out all the NALs\r\n for (let x = 0; x < nals.length; x++) {\r\n this.stream.write(H264_HEADER);\r\n this.stream.write(nals[x]);\r\n }\r\n }\r\n}\r\n"]} \ No newline at end of file diff --git a/dist/transports/SMPTE336MKLVTransport.d.ts b/dist/transports/SMPTE336MKLVTransport.d.ts new file mode 100644 index 0000000..e4e48d5 --- /dev/null +++ b/dist/transports/SMPTE336MKLVTransport.d.ts @@ -0,0 +1,20 @@ +/// +/// +import RTSPClient from "../RTSPClient"; +import { RTPPacket } from "../util"; +import * as transform from "sdp-transform"; +import { Writable } from "stream"; +interface Details { + codec: string; + mediaSource: transform.MediaDescription; + rtpChannel: number; + rtcpChannel: number; +} +export default class SMPTE336MKLVTransport { + client: RTSPClient; + stream: Writable; + rawData: Buffer[]; + constructor(client: RTSPClient, stream: Writable, details: Details); + processRTPPacket(packet: RTPPacket): void; +} +export {}; diff --git a/dist/transports/SMPTE336MKLVTransport.js b/dist/transports/SMPTE336MKLVTransport.js new file mode 100644 index 0000000..29bcb19 --- /dev/null +++ b/dist/transports/SMPTE336MKLVTransport.js @@ -0,0 +1,34 @@ +"use strict"; +// De-packetize RC 6597 RTP packets to re-create SMPTE336M KLV Metadata including STANAG 4609 +// Write data to a file as raw binary data. +// The RTP timestamp is not saved to the file. +// By Roger Hardiman, January 2026 +Object.defineProperty(exports, "__esModule", { value: true }); +class SMPTE336MKLVTransport { + constructor(client, stream, details) { + this.client = client; + this.stream = stream; + this.rawData = []; + client.on("data", (channel, data, packet) => { + if (channel == details.rtpChannel) { + this.processRTPPacket(packet); + } + }); + } + processRTPPacket(packet) { + // RTP Payload for ONVIF Metadata + // Accumulate payload + this.rawData.push(packet.payload); + if (packet.marker == 1) { // TODO... OR if the Timestamp has changed + // end of data. Write the file + // In this case we can just write each Buffer from the rawData array + // If we were passing the KLV to a caller, we would concatenate the Buffers in the rawData array first + for (const buffer of this.rawData) { + this.stream.write(buffer); + } + this.rawData = []; + } + } +} +exports.default = SMPTE336MKLVTransport; +//# sourceMappingURL=SMPTE336MKLVTransport.js.map \ No newline at end of file diff --git a/dist/transports/SMPTE336MKLVTransport.js.map b/dist/transports/SMPTE336MKLVTransport.js.map new file mode 100644 index 0000000..0b9ba05 --- /dev/null +++ b/dist/transports/SMPTE336MKLVTransport.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SMPTE336MKLVTransport.js","sourceRoot":"","sources":["../../lib/transports/SMPTE336MKLVTransport.ts"],"names":[],"mappings":";AAAA,6FAA6F;AAC7F,2CAA2C;AAC3C,8CAA8C;AAC9C,kCAAkC;;AAelC,MAAqB,qBAAqB;IAKxC,YAAY,MAAkB,EAAE,MAAgB,EAAE,OAAgB;QAChE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAElB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;YAC1C,IAAI,OAAO,IAAI,OAAO,CAAC,UAAU,EAAE;gBACjC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;aAC/B;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gBAAgB,CAAC,MAAiB;QAChC,iCAAiC;QAEjC,qBAAqB;QACrB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAEjC,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,EAAG,0CAA0C;YACnE,8BAA8B;YAC9B,oEAAoE;YACpE,sGAAsG;YACtG,KAAI,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE;gBAChC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;aAC3B;YACD,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;SACnB;IACH,CAAC;CACF;AAjCD,wCAiCC","sourcesContent":["// De-packetize RC 6597 RTP packets to re-create SMPTE336M KLV Metadata including STANAG 4609\r\n// Write data to a file as raw binary data.\r\n// The RTP timestamp is not saved to the file.\r\n// By Roger Hardiman, January 2026\r\n\r\nimport RTSPClient from \"../RTSPClient\";\r\nimport { RTPPacket } from \"../util\";\r\n\r\nimport * as transform from \"sdp-transform\";\r\nimport { Writable } from \"stream\";\r\n\r\ninterface Details {\r\n codec: string;\r\n mediaSource: transform.MediaDescription;\r\n rtpChannel: number;\r\n rtcpChannel: number;\r\n}\r\n\r\nexport default class SMPTE336MKLVTransport {\r\n client: RTSPClient;\r\n stream: Writable;\r\n rawData: Buffer[];\r\n\r\n constructor(client: RTSPClient, stream: Writable, details: Details) {\r\n this.client = client;\r\n this.stream = stream;\r\n this.rawData = [];\r\n\r\n client.on(\"data\", (channel, data, packet) => {\r\n if (channel == details.rtpChannel) {\r\n this.processRTPPacket(packet);\r\n }\r\n });\r\n }\r\n\r\n processRTPPacket(packet: RTPPacket): void {\r\n // RTP Payload for ONVIF Metadata\r\n\r\n // Accumulate payload\r\n this.rawData.push(packet.payload)\r\n\r\n if (packet.marker == 1) { // TODO... OR if the Timestamp has changed\r\n // end of data. Write the file\r\n // In this case we can just write each Buffer from the rawData array\r\n // If we were passing the KLV to a caller, we would concatenate the Buffers in the rawData array first\r\n for(const buffer of this.rawData) {\r\n this.stream.write(buffer);\r\n }\r\n this.rawData = [];\r\n }\r\n }\r\n}\r\n"]} \ No newline at end of file