diff --git a/CHANGELOG.md b/CHANGELOG.md index f446f39..6ba146b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Next Release +# 3.0.8 - 15th January 2027 +Add caching Digest Authentication code from Leone25 Enrico +Fix bug causing an error with Panasonic Cameras returning RTP/AVP/UDP in SETUP +Add -t to the example demo.js to pick the RTP transport (-t udp or -t tcp) +Handle multiple WWW-Authenticate responses (which is valid) and pick Digest over Basic + # 3.0.7 - 13th January 2027 Add H266 (VVC) support with output to a .266 file. Tested with https://github.com/jimm98y/SharpRealTimeStreaming which uses the SharpRTSP Library diff --git a/README.md b/README.md index 5aa09f7..d3f8c25 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ An example of most API features can be found at [examples/demo.js](examples/demo connect to a RTSP Stream and dump H264, H265, AV1 and AAC contents to a file. For example ```node examples\demo.js rtsp://myhostname/stream1``` -```node examples\demo.js -u username -p password rtsp://myhostname/stream1``` +```node examples\demo.js -u username -p password -t tcp rtsp://myhostname/stream1``` To testthe output file with a video player you can use FFMPEG's ffplay command ```ffplay outfile.264``` diff --git a/dist/RTSPClient copy.d.ts b/dist/RTSPClient copy.d.ts new file mode 100644 index 0000000..742cd6d --- /dev/null +++ b/dist/RTSPClient copy.d.ts @@ -0,0 +1,107 @@ +/// +/// +/// +/// +import * as net from "net"; +import * as tls from "tls"; +type SocketUnion = net.Socket | tls.TLSSocket; +import { EventEmitter } from "events"; +import * as util from "./util"; +import { Transport } from "./util"; +import * as transform from "sdp-transform"; +declare enum ReadStates { + SEARCHING = 0, + READING_RTSP_HEADER = 1, + READING_RTSP_PAYLOAD = 2, + READING_RAW_PACKET_SIZE = 3, + READING_RAW_PACKET = 4 +} +type Connection = "udp" | "tcp"; +type AuthOptions = { + type: "Digest" | "Basic"; + realm?: string; + nonce?: string; + qop?: string; + algorithm?: "MD5" | "SHA-256"; +}; +type Headers = { + [key: string]: string | number | undefined; + Session?: string; + Location?: string; + CSeq?: number; + "WWW-Authenticate"?: string; + Transport?: string; + Unsupported?: string; +}; +type Detail = { + codec: string; + mediaSource: ({ + type: string; + port: number; + protocol: string; + payloads?: string | undefined; + } & transform.MediaDescription); + transport: Transport['parameters']; + isH264: boolean; + rtpChannel: number; + rtcpChannel: number; + sr_ntpMSW?: number; + sr_ntpLSW?: number; + sr_rtptimestamp?: number; +}; +export default class RTSPClient extends EventEmitter { + username: string; + password: string; + headers: { + [key: string]: string; + }; + isConnected: boolean; + closed: boolean; + _url?: string; + _client?: SocketUnion; + _cSeq: number; + _unsupportedExtensions?: string[]; + _authOpions?: AuthOptions; + _session?: string; + _keepAliveID?: NodeJS.Timeout; + _nextFreeInterleavedChannel: number; + _nextFreeUDPPort: number; + readState: ReadStates; + messageBytes: number[]; + rtspContentLength: number; + rtspStatusLine: string; + rtspHeaders: Headers; + rtspPacketLength: number; + rtspPacket: Buffer; + rtspPacketPointer: number; + clientSSRC: number; + tcpSocket: SocketUnion; + setupResult: Array; + constructor(username: string, password: string, headers?: { + [key: string]: string; + }); + _netConnect(hostname: string, port: number, secure?: boolean): Promise; + connect(url: string, { keepAlive, connection, secure, }?: { + keepAlive: boolean; + connection?: Connection; + secure: boolean; + }): Promise; + request(requestName: string, headersParam?: Headers, url?: string): Promise<{ + headers: Headers; + mediaHeaders?: string[]; + } | void>; + respond(status: string, headersParam?: Headers): void; + play(): Promise; + pause(): Promise; + sendAudioBackChannel(audioChunk: Buffer): Promise; + close(isImmediate?: boolean): Promise; + _onData(data: Buffer): void; + _sendInterleavedData(channel: number, buffer: Buffer): void; + _sendUDPData(host: string, port: number, buffer: Buffer): void; + _emptyReceiverReport(): Buffer; + _socketWrite(socket: SocketUnion, data: Buffer): Promise; + private _generateAuthString; + ntpBaseDate_ms: number; + GetWallClockTime(packet: util.RTPPacket, detail: Detail): Date | undefined; +} +export { RTPPacket, RTCPPacket } from "./util"; diff --git a/dist/RTSPClient copy.js b/dist/RTSPClient copy.js new file mode 100644 index 0000000..ac53685 --- /dev/null +++ b/dist/RTSPClient copy.js @@ -0,0 +1,797 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const net = require("net"); +const tls = require("tls"); +const dgram = require("dgram"); +const url_1 = require("url"); +const events_1 = require("events"); +const util_1 = require("./util"); +const transform = require("sdp-transform"); +const RTPPacket_1 = require("./transports/RTPPacket"); +const RTP_AVP = "RTP/AVP"; +const RTP_AVPF = "RTP/AVPF"; // Used by AV1. This is RTP with Feedback (via RTCP) to request Keyframes via RTCP +const STATUS_OK = 200; +const STATUS_UNAUTH = 401; +// The WWW_AUTH is of the format +// TOKEN key=value +// TOKEN key1=value1,key2=value2 +// TOKEN key1="value1",key2=value2 +// RegEx reminder ? = Zero or One item +// * = Zero or More items +// + = 1 or More items +// \s is whitespace. But we need to 'escape the slash', hence \\s (or put the regex in / / characters) +// ?= is a lookahead +// The RegEx has two 'Groups' +// +// Group 1 (finding the Key) +// Look for one or more characters (a..z or A..Z) +// then look for whitespace +// then look for 'equals' +// then look for whitespace +// then look for an optional Quote character +// +// Group 2 (finding the Value) - +// Look for EITHER 'look backwards for a Quote', some characters, 'lookahead for a Quote' +// 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' +// OR some characters followed by 'optinal whitespace' +const WWW_AUTH = "WWW-Authenticate"; +const WWW_AUTH_REGEX = new RegExp('([a-zA-Z]+)\\s*=\\s*"?((?<=").*?(?=")|.*?(?=\\s*,?\\s*[a-zA-Z]+\\s*=)|.+[^\\s])', "g"); +var ReadStates; +(function (ReadStates) { + ReadStates[ReadStates["SEARCHING"] = 0] = "SEARCHING"; + ReadStates[ReadStates["READING_RTSP_HEADER"] = 1] = "READING_RTSP_HEADER"; + ReadStates[ReadStates["READING_RTSP_PAYLOAD"] = 2] = "READING_RTSP_PAYLOAD"; + ReadStates[ReadStates["READING_RAW_PACKET_SIZE"] = 3] = "READING_RAW_PACKET_SIZE"; + ReadStates[ReadStates["READING_RAW_PACKET"] = 4] = "READING_RAW_PACKET"; +})(ReadStates || (ReadStates = {})); +class RTSPClient extends events_1.EventEmitter { + constructor(username, password, headers) { + super(); + this.isConnected = false; + this.closed = false; + this._cSeq = 0; + this._nextFreeInterleavedChannel = 0; + this._nextFreeUDPPort = 5000; + this.readState = ReadStates.SEARCHING; + // Used as a cache for the data stream. + // What's in here is based on current #readState. + this.messageBytes = []; + // Used for parsing RTSP responses, + // Content-Length header in the RTSP message. + this.rtspContentLength = 0; + this.rtspStatusLine = ""; + this.rtspHeaders = {}; + // Used for parsing RTP/RTCP responses. + this.rtspPacketLength = 0; + this.rtspPacket = Buffer.from(""); + this.rtspPacketPointer = 0; + // Used in #_emptyReceiverReport. + this.clientSSRC = (0, util_1.generateSSRC)(); + this.tcpSocket = new net.Socket(); + this.setupResult = []; + this.ntpBaseDate_ms = new Date("1900/1/1").getTime(); + this.username = username; + this.password = password; + this.headers = Object.assign(Object.assign({}, (headers || {})), { "User-Agent": "yellowstone/3.x" }); + } + // This manages the lifecycle for the RTSP connection + // over TCP. + // + // Sets #_client. + // + // Handles receiving data & closing port, called during + // #connect. + _netConnect(hostname, port, secure = false) { + return new Promise((resolve, reject) => { + // Set after listeners defined. + const errorListener = (err) => { + client.removeListener("error", errorListener); + reject(err); + }; + const postConnectErrorListener = (err) => { + client.removeListener("error", postConnectErrorListener); + this.emit("error", err); + reject(err); + }; + const closeListener = () => { + client.removeListener("close", closeListener); + this.emit("close"); + this.close(true); + }; + const responseListener = (responseName, headers) => { + const name = responseName.split(" ")[0]; + if (name.indexOf("RTSP/") === 0) { + return; + } + if (name === "REDIRECT" || name === "ANNOUNCE") { + this.respond("200 OK", { CSeq: headers.CSeq }); + } + if (name === "REDIRECT" && headers.Location) { + this.close(); + this.connect(headers.Location); + } + }; + // rtsp or rtsps(with tls) + let client; + if (secure == false) { + client = net.connect(port, hostname, () => { + this.isConnected = true; + this._client = client; + client.removeListener("error", errorListener); + client.on("error", postConnectErrorListener); + this.on("response", responseListener); + resolve(this); + }); + } + else { + const options = { + rejectUnauthorized: false + }; + client = tls.connect(port, hostname, options, () => { + console.log("TLS Connection"); + this.isConnected = true; + this._client = client; + client.removeListener("error", errorListener); + this.on("response", responseListener); + resolve(this); + }); + } + client.on("data", this._onData.bind(this)); + client.on("error", errorListener); + client.on("close", closeListener); + this.tcpSocket = client; + }); + } + async connect(url, { keepAlive = true, connection = "udp", secure = false, } = { + keepAlive: true, + connection: "udp", + secure: false + }) { + const { hostname, port } = (0, url_1.parse)((this._url = url)); + if (!hostname) { + throw new Error("URL parsing error in connect method."); + } + const details = []; + await this._netConnect(hostname, parseInt(port || "554"), secure); + await this.request("OPTIONS"); + const describeRes = await this.request("DESCRIBE", { + Accept: "application/sdp", + }); + if (!describeRes || !describeRes.mediaHeaders) { + throw new Error("No media headers on DESCRIBE; RTSP server is broken (sanity check)"); + } + // For now, only RTP/AVP and RTP/AVPF are supported. (Some RTSPS servers use RTP/SAVP) + const { media } = transform.parse(describeRes.mediaHeaders.join("\r\n")); + // Loop over the Media Streams in the SDP looking for Video or Audio + // In theory the SDP can contain multiple Video and Audio Streams. We only want one of each type + let hasVideo = false; + let hasAudio = false; + let hasMetaData = false; + let hasBackchannel = false; + for (let x = 0; x < media.length; x++) { + let needSetup = false; + let codec = ""; + const mediaSource = media[x]; + // RFC says "If none of the direction attributes ("sendonly", "recvonly", "inactive", and "sendrecv") are present, + // the "sendrecv" SHOULD be assumed + if (mediaSource.direction == undefined) + mediaSource.direction = "sendrecv"; // Wowza does not send 'direction' + if (mediaSource.type === "video" && + mediaSource.protocol === RTP_AVP && + mediaSource.rtp[0].codec === "H264") { + this.emit("log", "H264 Video Stream Found in SDP", ""); + if (hasVideo == false) { + needSetup = true; + hasVideo = true; + codec = "H264"; + } + } + if (mediaSource.type === "video" && + mediaSource.protocol === RTP_AVP && + mediaSource.rtp[0].codec === "H265") { + this.emit("log", "H265 Video Stream Found in SDP", ""); + if (hasVideo == false) { + needSetup = true; + hasVideo = true; + codec = "H265"; + } + } + if (mediaSource.type === "video" && + mediaSource.protocol === RTP_AVP && + mediaSource.rtp[0].codec === "H266") { + this.emit("log", "H266 Video Stream Found in SDP", ""); + if (hasVideo == false) { + needSetup = true; + hasVideo = true; + codec = "H266"; + } + } + if (mediaSource.type === "video" && + (mediaSource.protocol === RTP_AVP || mediaSource.protocol === RTP_AVPF) && + mediaSource.rtp[0].codec === "AV1") { + this.emit("log", "AV1 Video Stream Found in SDP", ""); + if (hasVideo == false) { + needSetup = true; + hasVideo = true; + codec = "AV1"; + } + } + if (mediaSource.type === "audio" && + (mediaSource.direction === "recvonly" || mediaSource.direction === "sendrecv") && + mediaSource.protocol === RTP_AVP && + mediaSource.rtp[0].codec.toLowerCase() === "mpeg4-generic" && // (RFC examples are lower case. Axis cameras use upper case) + mediaSource.fmtp[0].config.includes("AAC")) { + this.emit("log", "AAC Audio Stream Found in SDP", ""); + if (hasAudio == false) { + needSetup = true; + hasAudio = true; + codec = "AAC"; + } + } + if (mediaSource.type === "audio" && + mediaSource.direction === "sendonly" && + mediaSource.protocol === RTP_AVP) { + this.emit("log", "Audio backchannel Found in SDP", ""); + if (hasBackchannel == false) { + needSetup = true; + hasBackchannel = true; + codec = mediaSource.rtp[0].codec; + } + } + if (mediaSource.type === "application" && + mediaSource.protocol === RTP_AVP && + mediaSource.rtp[0].codec.toLowerCase() === "vnd.onvif.metadata") { + this.emit("log", "ONVIF Meta Data Found in SDP", ""); + if (hasMetaData == false) { + needSetup = true; + hasMetaData = true; + codec = "vnd.onvif.metadata"; + } + } + if (needSetup) { + let streamurl = ""; + // The 'control' in the SDP can be a relative or absolute uri + if (mediaSource.control) { + if (mediaSource.control.toLowerCase().startsWith("rtsp://")) { + // absolute path + streamurl = mediaSource.control; + } + else { + // relative path + streamurl = this._url + "/" + mediaSource.control; + } + } + // Perform a SETUP on the streamurl + // either 'udp' RTP/RTCP packets + // or with 'tcp' RTP/TCP packets which are interleaved into the TCP based RTSP socket + let setupRes; + let rtpChannel; + let rtcpChannel; + let rtpReceiver = null; // UDP mode init value + let rtcpReceiver = null; // UDP mode init value + if (connection === "udp") { + // Create a pair of UDP listeners, even numbered port for RTP + // and odd numbered port for RTCP + rtpChannel = this._nextFreeUDPPort; + rtcpChannel = this._nextFreeUDPPort + 1; + this._nextFreeUDPPort += 2; + const rtpPort = rtpChannel; + rtpReceiver = dgram.createSocket("udp4"); + rtpReceiver.on("message", (buf, remote) => { + let packet = (0, util_1.parseRTPPacket)(buf); + // Add wall clock time + const detail = this.setupResult.find(item => item.rtpChannel == rtpChannel); + if (detail != undefined) + packet.wallclockTime = this.GetWallClockTime(packet, detail); + this.emit("data", rtpPort, packet.payload, packet); + }); + const rtcpPort = rtcpChannel; + rtcpReceiver = dgram.createSocket("udp4"); + rtcpReceiver.on("message", (buf, remote) => { + const packet = (0, util_1.parseRTCPPacket)(buf); + // If this is a Sender Report, cache the NTP Wall Clock data + if (packet.packetType == 200 && packet.senderReport != undefined) { + let detail = this.setupResult.find(item => item.rtcpChannel == rtcpChannel); + if (detail != undefined) { + detail.sr_ntpMSW = packet.senderReport.ntpTimestampMSW; + detail.sr_ntpLSW = packet.senderReport.ntpTimestampLSW; + detail.sr_rtptimestamp = packet.senderReport.rtpTimestamp; + } + } + this.emit("controlData", rtcpPort, packet); + const receiver_report = this._emptyReceiverReport(); + this._sendUDPData(remote.address, remote.port, receiver_report); + }); + // Block until both UDP sockets are open. + await new Promise((resolve) => { + rtpReceiver === null || rtpReceiver === void 0 ? void 0 : rtpReceiver.bind(rtpPort, () => resolve({})); + }); + await new Promise((resolve) => { + rtcpReceiver === null || rtcpReceiver === void 0 ? void 0 : rtcpReceiver.bind(rtcpPort, () => resolve({})); + }); + const setupHeader = { + Transport: `RTP/AVP;unicast;client_port=${rtpPort}-${rtcpPort}`, + }; + if (this._session) + Object.assign(setupHeader, { Session: this._session }); + setupRes = await this.request("SETUP", setupHeader, streamurl); + } + else if (connection === "tcp") { + // channel 0, RTP + // channel 1, RTCP + rtpChannel = this._nextFreeInterleavedChannel; + rtcpChannel = this._nextFreeInterleavedChannel + 1; + this._nextFreeInterleavedChannel += 2; + const setupHeader = { + Transport: `RTP/AVP/TCP;interleaved=${rtpChannel}-${rtcpChannel}`, + }; + if (this._session) + Object.assign(setupHeader, { Session: this._session }); // not used on first SETUP + setupRes = await this.request("SETUP", setupHeader, streamurl); + } + else { + throw new Error(`Connection parameter to RTSPClient#connect is ${connection}, not udp or tcp!`); + } + if (!setupRes) { + throw new Error("No SETUP response; RTSP server is broken (sanity check)"); + } + const { headers } = setupRes; + if (!headers.Transport) { + throw new Error("No Transport header on SETUP; RTSP server is broken (sanity check)"); + } + const transport = (0, util_1.parseTransport)(headers.Transport); + if (transport.protocol !== "RTP/AVP/TCP" && + transport.protocol !== "RTP/AVP") { + throw new Error("Only RTSP servers supporting RTP/AVP(unicast) or RTP/AVP/TCP are supported at this time."); + } + // Patch from zoolyka (Zoltan Hajdu). + // Try to open a hole in the NAT router (to allow incoming UDP packets) + // by send a UDP packet for RTP and RTCP to the remote RTSP server. + // Note, Roger did not have a router that needed this so the feature is untested. + // May be better to change the RTCP message to a Receiver Report, leaving the RTP message as zero bytes + if (connection === "udp" && transport && rtpReceiver && rtcpReceiver) { + rtpReceiver.send(Buffer.from(''), Number(transport.parameters["server_port"].split("-")[0]), hostname); + rtcpReceiver.send(Buffer.from(''), Number(transport.parameters["server_port"].split("-")[1]), hostname); + } + if (headers.Unsupported) { + this._unsupportedExtensions = headers.Unsupported.split(","); + } + if (headers.Session) { + this._session = headers.Session.split(";")[0]; + } + const detail = { + codec, + mediaSource, + transport: transport.parameters, + isH264: codec === "H264", + rtpChannel, + rtcpChannel, + }; + details.push(detail); + } // end if (needSetup) + } // end for loop, looping over each media stream + if (keepAlive) { + // Start a Timer to send OPTIONS every 20 seconds to keep stream alive + // using the Session ID + this._keepAliveID = setInterval(() => { + this.request("OPTIONS", { Session: this._session }); + // this.request("OPTIONS"); + }, 20 * 1000); + } + this.setupResult = details; + return details; + } + request(requestName, headersParam = {}, url) { + if (!this._client) { + return Promise.resolve(); + } + if (!url) { + url = this._url; + } + const id = ++this._cSeq; + // mutable via string addition + let req = `${requestName} ${url} RTSP/1.0\r\nCSeq: ${id}\r\n`; + const headers = Object.assign(Object.assign({}, this.headers), headersParam); + if (this._authOpions) { + Object.assign(headers, { + Authorization: this._generateAuthString(requestName, url), + }); + // TESTING - now mess up the cached auth info + console.log("AAAAAAAAAAAAAAAAAAAAAAAAAAA"); + } + // NOTE: + // If we cache the Authenitcation Type (Direct or Basic) then we could + // re-compute an Authorization Header here and include in the RTSP Command + // This would make connections a faster with fewer round-trips to the RTSP Server + req += Object.entries(headers) + .map(([key, value]) => `${key}: ${value}\r\n`) + .join(""); + this.emit("log", req, "C->S"); + // Make sure to add an empty line after the request. + this._client.write(`${req}\r\n`); + return new Promise((resolve, reject) => { + const responseHandler = (responseName, resHeaders, mediaHeaders) => { + const firstAnswer = String(resHeaders[""]) || ""; + if (firstAnswer.indexOf("401") >= 0 && 'Authorization' in headers) { + // If the RTSP Command we sent included an Authorization and we have 401 error, then reject() + reject(new Error(`Bad RTSP credentials!`)); + return; + } + if (resHeaders.CSeq !== id) { + return; + } + this.removeListener("response", responseHandler); + const statusCode = parseInt(responseName.split(" ")[1]); + if (statusCode === STATUS_OK) { + if (mediaHeaders.length > 0) { + resolve({ + headers: resHeaders, + mediaHeaders, + }); + } + else { + resolve({ + headers: resHeaders, + }); + } + } + else { + const authHeader = resHeaders[WWW_AUTH]; + // We have status code unauthenticated. + if (statusCode === STATUS_UNAUTH && authHeader) { + this._authOpions = { + type: authHeader.split(" ")[0], + algorithm: "MD5", // Default to MD5 if no algorthm is given. Milestone's RTSP server also supports SHA-256 for FIPS + }; + // Get auth properties from WWW_AUTH header. + let match = WWW_AUTH_REGEX.exec(authHeader); + while (match != null) { + const prop = match[1]; + if (prop == "realm" && match[2]) { + this._authOpions.realm = match[2]; + } + if (prop == "nonce" && match[2]) { + this._authOpions.nonce = match[2]; + } + if (prop == "algorithm" && match[2]) { + this._authOpions.algorithm = match[2]; + } + if (prop == "qop" && match[2]) { + this._authOpions.qop = match[2]; + } + match = WWW_AUTH_REGEX.exec(authHeader); + } + // Repeat the request, now _authOptions will be detected and the Authorization header will be generated + resolve(this.request(requestName, headers, url)); + return; + } + reject(new Error(`Bad RTSP status code ${statusCode}!`)); + return; + } + }; + this.on("response", responseHandler); + }); + } + respond(status, headersParam = {}) { + if (!this._client) { + return; + } + // mutable via string addition + let res = `RTSP/1.0 ${status}\r\n`; + const headers = Object.assign(Object.assign({}, this.headers), headersParam); + res += Object.entries(headers) + .map(([key, value]) => `${key}: ${value}\r\n`) + .join(""); + this.emit("log", res, "C->S"); + this._client.write(`${res}\r\n`); + } + async play() { + if (!this.isConnected) { + throw new Error("Client is not connected."); + } + await this.request("PLAY", { Session: this._session }); + } + async pause() { + if (!this.isConnected) { + throw new Error("Client is not connected."); + } + await this.request("PAUSE", { Session: this._session }); + } + async sendAudioBackChannel(audioChunk) { + let rtp, buf; + const bufSize = 160; + while (audioChunk.length > 0) { + if (audioChunk.length > bufSize) { + buf = audioChunk.slice(0, bufSize); + audioChunk = audioChunk.slice(bufSize, audioChunk.length); + } + else { + buf = audioChunk.slice(0, audioChunk.length); + audioChunk = Buffer.from([]); + } + if (!rtp) + rtp = new RTPPacket_1.default(buf); + else + rtp.payload = buf; + // rtp.type = 8;// set động + rtp.time += buf.length; + rtp.seq++; + const bufferLength = Buffer.alloc(2); + bufferLength.writeUInt16BE(rtp.packet.length, 0); + let channelInterleaved = this.setupResult.filter((value) => { + return value.mediaSource.type === 'audio' && value.mediaSource.direction === 'sendonly'; + })[0].transport.interleaved; + /* RTSP Interleaved Frame structure + |dollar sign|channel identifier|data length| + |1 Byte |1 Byte |2 Bytes | + */ + channelInterleaved = channelInterleaved.split('-')[0]; + let interleavedHeader = Buffer.from([0x24]); // set '$' + interleavedHeader = Buffer.concat([interleavedHeader, Buffer.from([channelInterleaved])]); + interleavedHeader = Buffer.concat([interleavedHeader, bufferLength]); + const dataToSend = Buffer.concat([interleavedHeader, rtp.packet]); + await this._socketWrite(this.tcpSocket, dataToSend); + } + return; + } + async close(isImmediate = false) { + if (this.closed) + return; + this.closed = true; + if (!this._client) { + return; + } + if (!isImmediate) { + await this.request("TEARDOWN", { + Session: this._session, + }); + } + this._client.end(); + this.removeAllListeners("response"); + if (this._keepAliveID != undefined) { + clearInterval(this._keepAliveID); + this._keepAliveID = undefined; + } + this.isConnected = false; + this._cSeq = 0; + } + _onData(data) { + let index = 0; + // $ + const PACKET_START = 0x24; + // R + const RTSP_HEADER_START = 0x52; + // /n + const ENDL = 10; + while (index < data.length) { + // read RTP or RTCP packet + if (this.readState == ReadStates.SEARCHING && + data[index] == PACKET_START) { + this.messageBytes = [data[index]]; + index++; + this.readState = ReadStates.READING_RAW_PACKET_SIZE; + } + else if (this.readState == ReadStates.READING_RAW_PACKET_SIZE) { + // accumulate bytes for $, channel and length + this.messageBytes.push(data[index]); + index++; + if (this.messageBytes.length == 4) { + this.rtspPacketLength = + (this.messageBytes[2] << 8) + this.messageBytes[3]; + if (this.rtspPacketLength > 0) { + this.rtspPacket = Buffer.alloc(this.rtspPacketLength); + this.rtspPacketPointer = 0; + this.readState = ReadStates.READING_RAW_PACKET; + } + else { + this.readState = ReadStates.SEARCHING; + } + } + } + else if (this.readState == ReadStates.READING_RAW_PACKET) { + this.rtspPacket[this.rtspPacketPointer++] = data[index]; + index++; + if (this.rtspPacketPointer == this.rtspPacketLength) { + const packetChannel = this.messageBytes[1]; + if ((packetChannel & 0x01) === 0) { + // even number + let packet = (0, util_1.parseRTPPacket)(this.rtspPacket); + // Get the Session Detail + const detail = this.setupResult.find(item => item.rtpChannel == packetChannel); + if (detail != undefined) + packet.wallclockTime = this.GetWallClockTime(packet, detail); + this.emit("data", packetChannel, packet.payload, packet); + } + if ((packetChannel & 0x01) === 1) { + // odd number + const packet = (0, util_1.parseRTCPPacket)(this.rtspPacket); + // If this is a Sender Report, cache the NTP Wall Clock data + if (packet.packetType == 200 && packet.senderReport != undefined) { + let detail = this.setupResult.find(item => item.rtcpChannel == packetChannel); + if (detail != undefined) { + detail.sr_ntpMSW = packet.senderReport.ntpTimestampMSW; + detail.sr_ntpLSW = packet.senderReport.ntpTimestampLSW; + detail.sr_rtptimestamp = packet.senderReport.rtpTimestamp; + } + } + this.emit("controlData", packetChannel, packet); + const receiver_report = this._emptyReceiverReport(); + this._sendInterleavedData(packetChannel, receiver_report); + } + this.readState = ReadStates.SEARCHING; + } + // read response data + } + else if (this.readState == ReadStates.SEARCHING && + data[index] == RTSP_HEADER_START) { + // found the start of a RTSP rtsp_message + this.messageBytes = [data[index]]; + index++; + this.readState = ReadStates.READING_RTSP_HEADER; + } + else if (this.readState == ReadStates.READING_RTSP_HEADER) { + // Reading a RTSP message. + // Add character to the messageBytes + // Ignore /r (13) but keep /n (10) + if (data[index] != 13) { + this.messageBytes.push(data[index]); + } + index++; + // if we have two new lines back to back then we have a complete RTSP command, + // note we may still need to read the Content Payload (the body) e.g. the SDP + if (this.messageBytes.length >= 2 && + this.messageBytes[this.messageBytes.length - 2] == ENDL && + this.messageBytes[this.messageBytes.length - 1] == ENDL) { + // Parse the Header + const text = String.fromCharCode.apply(null, this.messageBytes); + const lines = text.split("\n"); + this.rtspContentLength = 0; + this.rtspStatusLine = lines[0]; + this.rtspHeaders = {}; + lines.forEach((line) => { + const indexOf = line.indexOf(":"); + if (indexOf !== line.length - 1) { + const key = line.substring(0, indexOf).trim(); + const data = line.substring(indexOf + 1).trim(); + this.rtspHeaders[key] = + key != "Session" && data.match(/^[0-9]+$/) + ? parseInt(data, 10) + : data; + // workaround for buggy Hipcam RealServer/V1.0 camera which returns Content-length and not Content-Length + if (key.toLowerCase() == "content-length") { + this.rtspContentLength = parseInt(data, 10); + } + } + }); + // if no content length, there there's no media headers + // emit the message + if (!this.rtspContentLength) { + this.emit("log", text, "S->C"); + this.emit("response", this.rtspStatusLine, this.rtspHeaders, []); + this.readState = ReadStates.SEARCHING; + } + else { + this.messageBytes = []; + this.readState = ReadStates.READING_RTSP_PAYLOAD; + } + } + } + else if (this.readState == ReadStates.READING_RTSP_PAYLOAD && + this.messageBytes.length < this.rtspContentLength) { + // Copy data into the RTSP payload + this.messageBytes.push(data[index]); + index++; + if (this.messageBytes.length == this.rtspContentLength) { + const text = String.fromCharCode.apply(null, this.messageBytes); + const mediaHeaders = text.split("\n"); + // Emit the RTSP message + this.emit("log", String.fromCharCode.apply(null, this.messageBytes) + text, "S->C"); + this.emit("response", this.rtspStatusLine, this.rtspHeaders, mediaHeaders); + this.readState = ReadStates.SEARCHING; + } + } + else { + // unexpected data + throw new Error("Bug in RTSP data framing, please file an issue with the author with stacktrace."); + } + } // end while + } + _sendInterleavedData(channel, buffer) { + if (!this._client) { + return; + } + const req = `${buffer.length} bytes of interleaved data on channel ${channel}`; + this.emit("log", req, "C->S"); + const header = Buffer.alloc(4); + header[0] = 0x24; // ascii $ + header[1] = channel; + header[2] = (buffer.length >> 8) & 0xff; + header[3] = (buffer.length >> 0) & 0xff; + const data = Buffer.concat([header, buffer]); + this._client.write(data); + } + _sendUDPData(host, port, buffer) { + const udp = dgram.createSocket("udp4"); + udp.send(buffer, 0, buffer.length, port, host, (err, bytes) => { + // TODO: Don't ignore errors. + udp.close(); + }); + } + _emptyReceiverReport() { + const report = Buffer.alloc(8); + const version = 2; + const paddingBit = 0; + const reportCount = 0; // an empty report + const packetType = 201; // Receiver Report + const length = report.length / 4 - 1; // num 32 bit words minus 1 + report[0] = (version << 6) + (paddingBit << 5) + reportCount; + report[1] = packetType; + report[2] = (length >> 8) & 0xff; + report[3] = (length >> 0) & 0xff; + report[4] = (this.clientSSRC >> 24) & 0xff; + report[5] = (this.clientSSRC >> 16) & 0xff; + report[6] = (this.clientSSRC >> 8) & 0xff; + report[7] = (this.clientSSRC >> 0) & 0xff; + return report; + } + async _socketWrite(socket, data) { + return new Promise((resolve, reject) => { + setTimeout(() => { + socket.write(data, (error) => { + if (error) { + reject(error); + } + else { + resolve(undefined); + } + }); + }, 20); + }); + } + _generateAuthString(requestName, url) { + if (!url) { + url = this._url; + } + let authString = ""; + if (!this._authOpions) + return ""; + if (this._authOpions.type === "Digest") { + // Digest Authentication + // Select Hash Function, default to MD5 + const HashFunction = (this._authOpions.algorithm == "SHA-256" ? util_1.getSHA256Hash : util_1.getMD5Hash); + const ha1 = HashFunction(`${this.username}:${this._authOpions.realm}:${this.password}`); + const ha2 = HashFunction(`${requestName}:${url}`); + const ha3 = HashFunction(`${ha1}:${this._authOpions.nonce}:${ha2}`); + // Some RTSP servers to not accept "algorithm=NNN" in the authString and reject the authentication. So only add algorithm=ZZZZ when not using MD5 + if (this._authOpions.algorithm == "MD5") + authString = `Digest username="${this.username}",realm="${this._authOpions.realm}",nonce="${this._authOpions.nonce}",uri="${url}",response="${ha3}"`; + else + authString = `Digest username="${this.username}",realm="${this._authOpions.realm}",nonce="${this._authOpions.nonce}",algorithm=${this._authOpions.algorithm},uri="${url}",response="${ha3}"`; + if (this._authOpions.qop != null) + authString += `, qop="${this._authOpions.qop}"`; + } + else if (this._authOpions.type === "Basic") { + // Basic Authentication + // https://xkcd.com/538/ + const b64 = Buffer.from(`${this.username}:${this.password}`).toString("base64"); + authString = `Basic ${b64}`; + } + return authString; + } + // Note we have had a RTP Packet in Yellowstone for many years, but the Audio Backchennal code added another object also called RTPPacket + GetWallClockTime(packet, detail) { + // Add Wall Clock Time + if (detail.sr_ntpMSW != undefined && detail.sr_ntpLSW != undefined && detail.sr_rtptimestamp != undefined && detail.mediaSource.rtp[0].rate != undefined) { + let refTimestampSecs = detail.sr_rtptimestamp / detail.mediaSource.rtp[0].rate; // H264 is 90 kHz clock rate + let packetTimestampSecs = packet.timestamp / detail.mediaSource.rtp[0].rate; // eg 90kHz + let packetTimestampDeltaSecs = packetTimestampSecs - refTimestampSecs; + let refTimestamp = new Date(this.ntpBaseDate_ms + (detail.sr_ntpMSW * 1000) + ((detail.sr_ntpLSW / Math.pow(2, 32)) * 1000)); + let wallclockTime = new Date(refTimestamp.getTime() + (packetTimestampDeltaSecs * 1000)); + return wallclockTime; + } + // Could not generate a Wall Clock Time + return undefined; + } +} +exports.default = RTSPClient; +//# sourceMappingURL=RTSPClient%20copy.js.map \ No newline at end of file diff --git a/dist/RTSPClient copy.js.map b/dist/RTSPClient copy.js.map new file mode 100644 index 0000000..7ae47ec --- /dev/null +++ b/dist/RTSPClient copy.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RTSPClient copy.js","sourceRoot":"","sources":["../lib/RTSPClient copy.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 qop?: 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 transport.protocol !== \"RTP/AVP/TCP\" &&\r\n transport.protocol !== \"RTP/AVP\"\r\n ) {\r\n throw new Error(\r\n \"Only RTSP servers supporting RTP/AVP(unicast) 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 // TESTING - now mess up the cached auth info\r\n console.log(\"AAAAAAAAAAAAAAAAAAAAAAAAAAA\");\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 if (prop == \"qop\" && match[2]) {\r\n this._authOpions.qop = match[2];\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 this.rtspHeaders[key] =\r\n key != \"Session\" && data.match(/^[0-9]+$/)\r\n ? parseInt(data, 10)\r\n : data;\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\r\n if (this._authOpions.qop != null)\r\n authString += `, qop=\"${this._authOpions.qop}\"`;\r\n\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\";"]} \ No newline at end of file diff --git a/dist/RTSPClient.js b/dist/RTSPClient.js index 590443b..f3ace84 100644 --- a/dist/RTSPClient.js +++ b/dist/RTSPClient.js @@ -340,9 +340,15 @@ class RTSPClient extends events_1.EventEmitter { throw new Error("No Transport header on SETUP; RTSP server is broken (sanity check)"); } const transport = (0, util_1.parseTransport)(headers.Transport); - if (transport.protocol !== "RTP/AVP/TCP" && - transport.protocol !== "RTP/AVP") { - throw new Error("Only RTSP servers supporting RTP/AVP(unicast) or RTP/AVP/TCP are supported at this time."); + if ( + // TCP + transport.protocol !== "RTP/AVP/TCP" && + // UDP + transport.protocol !== "RTP/AVP" && + // UDP + transport.protocol !== "RTP/AVP/UDP" // Panasonic cameras send this + ) { + throw new Error("Only RTSP servers supporting RTP/AVP or RTP/AVP/UDP or RTP/AVP/TCP are supported at this time."); } // Patch from zoolyka (Zoltan Hajdu). // Try to open a hole in the NAT router (to allow incoming UDP packets) @@ -644,14 +650,35 @@ class RTSPClient extends events_1.EventEmitter { this.rtspStatusLine = lines[0]; this.rtspHeaders = {}; lines.forEach((line) => { + var _a; const indexOf = line.indexOf(":"); if (indexOf !== line.length - 1) { const key = line.substring(0, indexOf).trim(); const data = line.substring(indexOf + 1).trim(); - this.rtspHeaders[key] = - key != "Session" && data.match(/^[0-9]+$/) - ? parseInt(data, 10) - : data; + if (key == "Session") + this.rtspHeaders[key] = data; + else if (key == "WWW-Authenticate") { + // Handle multiple WWW-Authenticate entries and pick the 'best' + // We prefer 'Digest' over 'Basic' + // We preger 'Digest SHAxxx' over 'Digest MD5' or 'Digest with no algorithm (defaults to MD5) (STILL TODO) + if (key in this.rtspHeaders) { + console.log("Duplicate WWW-Authenticate keys"); + if (data.startsWith("Digest") && ((_a = this.rtspHeaders[key]) === null || _a === void 0 ? void 0 : _a.startsWith("Basic"))) { + this.rtspHeaders[key] = data; // Replace Basic with Digest + } + console.log("Keeping WWW-Authenticate: " + this.rtspHeaders[key]); + } + else { + this.rtspHeaders[key] = data; + } + } + else { + // Store the result as either a String type or a Number type + this.rtspHeaders[key] = + data.match(/^[0-9]+$/) + ? parseInt(data, 10) + : data; + } // workaround for buggy Hipcam RealServer/V1.0 camera which returns Content-length and not Content-Length if (key.toLowerCase() == "content-length") { this.rtspContentLength = parseInt(data, 10); diff --git a/dist/RTSPClient.js.map b/dist/RTSPClient.js.map index b77c308..c549169 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":"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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 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 // 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 transport.protocol !== \"RTP/AVP/TCP\" &&\r\n transport.protocol !== \"RTP/AVP\"\r\n ) {\r\n throw new Error(\r\n \"Only RTSP servers supporting RTP/AVP(unicast) 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 const id = ++this._cSeq;\r\n // mutable via string addition\r\n let req = `${requestName} ${url || this._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 // 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 const type = authHeader.split(\" \")[0];\r\n\r\n // Get auth properties from WWW_AUTH header.\r\n let realm = \"\";\r\n let nonce = \"\";\r\n let algorithm = \"MD5\"; // Default to MD5 if no algorthm is given. Milestone's RTSP server also supports SHA-256 for FIPS\r\n\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 realm = match[2];\r\n }\r\n\r\n if (prop == \"nonce\" && match[2]) {\r\n nonce = match[2];\r\n }\r\n\r\n if (prop == \"algorithm\" && match[2]) {\r\n algorithm = match[2];\r\n }\r\n\r\n match = WWW_AUTH_REGEX.exec(authHeader);\r\n }\r\n\r\n // mutable, corresponds to Authorization header\r\n let authString = \"\";\r\n\r\n if (type === \"Digest\") {\r\n // Digest Authentication\r\n\r\n // Select Hash Function, default to MD5\r\n const HashFunction = (algorithm == \"SHA-256\" ? getSHA256Hash : getMD5Hash);\r\n\r\n const ha1 = HashFunction(\r\n `${this.username}:${realm}:${this.password}`\r\n );\r\n const ha2 = HashFunction(`${requestName}:${this._url}`);\r\n const ha3 = HashFunction(`${ha1}:${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 (algorithm == \"MD5\")\r\n authString = `Digest username=\"${this.username}\",realm=\"${realm}\",nonce=\"${nonce}\",uri=\"${this._url}\",response=\"${ha3}\"`;\r\n else\r\n authString = `Digest username=\"${this.username}\",realm=\"${realm}\",nonce=\"${nonce}\",algorithm=${algorithm},uri=\"${this._url}\",response=\"${ha3}\"`;\r\n } else if (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\r\n Object.assign(headers, {\r\n Authorization: authString,\r\n });\r\n\r\n resolve(this.request(requestName, headers, url)); // Call this.request with Authorized request\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 this.rtspHeaders[key] =\r\n key != \"Session\" && data.match(/^[0-9]+$/)\r\n ? parseInt(data, 10)\r\n : data;\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 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"]} +{"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 (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/util.js.map b/dist/util.js.map index da8143f..43e3af6 100644 --- a/dist/util.js.map +++ b/dist/util.js.map @@ -1 +1 @@ -{"version":3,"file":"util.js","sourceRoot":"","sources":["../lib/util.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"crypto\";\n\nexport interface RTPPacket {\n id: number;\n timestamp: number;\n marker: number;\n padding: number;\n hasExtensions: number;\n\n payload: Buffer;\n\n length: number;\n paddingLength: number;\n\n payloadType: number;\n\n // Additional information added to the Packet\n wallclockTime?: Date;\n}\n\nexport function parseRTPPacket(buffer: Buffer): RTPPacket {\n const padding = (buffer[0] >> 5) & 0x01\n let paddingLength = 0;\n if (padding == 1) {\n // padding size is the last byte of the RTP data\n paddingLength = buffer[buffer.length - 1];\n }\n const hasExtensions = (buffer[0] >> 4) & 0x01;\n const marker = (buffer[1]) >>> 7;\n const payloadType = buffer[1] & 0x7f;\n const num_csrc_identifiers = (buffer[0] & 0x0F);\n\n const payload = buffer.slice((num_csrc_identifiers * 4) + (hasExtensions ? 16 : 12)); // includes padding\n const length = payload.length;\n\n\n return {\n id: buffer.readUInt16BE(2),\n timestamp: buffer.readUInt32BE(4),\n marker,\n padding,\n payloadType,\n hasExtensions,\n payload,\n length,\n paddingLength,\n };\n}\n\nexport interface SenderReport {\n ntpTimestampMSW: number;\n ntpTimestampLSW: number;\n rtpTimestamp: number;\n senderPacketCount: number;\n senderOctetCount: number;\n}\n\nexport interface RTCPPacket {\n buffer: Buffer;\n version: number;\n padding: number;\n receptionReportCount: number;\n packetType: number;\n length: number;\n ssrc: number;\n\n senderReport?: SenderReport;\n}\n\nexport function parseRTCPPacket(buffer: Buffer): RTCPPacket {\n\n // Packet Types\n // SR Sender Report 200\n // RR Receiver Report 201\n // SDES Source Description 202\n // BYE Goodbye 203\n // APP Application-Defined 204\n // RTPFB Generic RTP feedback 205\n // PSFB Payload-specific feedback 206\n // XR RTCP Extension 207\n const version = (buffer[0] >> 6);\n const padding = (buffer[0] >> 5) & 0x01;\n const receptionReportCount = (buffer[0]) & 0x1F;\n const packetType = buffer[1];\n const length = buffer[2] << 8 + buffer[3]; // The length in 32 bit words (not the length in bytes)\n const ssrc = buffer[4] << 24 + buffer[5] << 16 + buffer[6] << 8 + buffer[7];\n\n let result: RTCPPacket = {\n buffer,\n version,\n padding,\n length,\n ssrc,\n receptionReportCount,\n packetType};\n\n if (packetType == 200) {\n let senderReport: SenderReport = {\n ntpTimestampMSW: buffer.readUInt32BE(8),\n ntpTimestampLSW: buffer.readUInt32BE(12),\n rtpTimestamp: buffer.readUInt32BE(16),\n senderPacketCount: buffer.readUInt32BE(20),\n senderOctetCount: buffer.readUInt32BE(24)\n };\n\n result.senderReport = senderReport;\n }\n\n return result;\n}\n\n// utility function for using crypto library\nexport function getMD5Hash(str: string): string {\n const md5 = createHash(\"md5\");\n md5.update(str);\n\n return md5.digest(\"hex\");\n}\n\nexport function getSHA256Hash(str: string): string {\n const sha256 = createHash(\"sha256\"); // use getHashes() to see what is supported\n sha256.update(str);\n\n return sha256.digest(\"hex\");\n}\n\nexport interface Transport {\n protocol: string;\n parameters: { [key: string]: string };\n}\n\nexport function parseTransport(transport: string): Transport {\n const parameters: { [key: string]: string } = {};\n\n const parts = transport.split(\";\");\n const protocol = parts[0];\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const index = part.indexOf(\"=\");\n\n if (index > -1 && index !== part.length - 1) {\n parameters[part.substring(0, index)] = part.substring(index + 1);\n }\n }\n\n return {\n protocol,\n parameters\n };\n}\n\nexport function randInclusive(min: number, max: number): number {\n min = Math.ceil(min);\n max = Math.floor(max);\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nexport function generateSSRC(): number {\n return randInclusive(1, 0xffffffff);\n}\n\n// BitStream classes by 2018 Roger Hardiman, RJH Technical Consultancy Ltd\n// Write to a bitstream and read back as an array\n\nexport class BitStream {\n\n data: number[] = []; // Array only stores 0 or 1 (one 'bit' per buffer item)\n // not very efficient on memory\n\n // Constructor\n // constructor() {}\n \n // Functions\n AddValue(value: number, num_bits: number): void {\n // Add each bit to the List\n for (let i = num_bits - 1; i >= 0; i--) {\n this.data.push((value >> i) & 0x01);\n }\n }\n\n AddHexString(hex_string: string): void {\n const hex_chars = hex_string.toUpperCase();\n\n for (let x = 0; x < hex_chars.length; x++) {\n const c = hex_chars.charAt(x);\n if (c == '0') this.AddValue(0, 4);\n else if (c == '1') this.AddValue(1, 4);\n else if (c == '2') this.AddValue(2, 4);\n else if (c == '3') this.AddValue(3, 4);\n else if (c == '4') this.AddValue(4, 4);\n else if (c == '5') this.AddValue(5, 4);\n else if (c == '6') this.AddValue(6, 4);\n else if (c == '7') this.AddValue(7, 4);\n else if (c == '8') this.AddValue(8, 4);\n else if (c == '9') this.AddValue(9, 4);\n else if (c == 'A') this.AddValue(10, 4);\n else if (c == 'B') this.AddValue(11, 4);\n else if (c == 'C') this.AddValue(12, 4);\n else if (c == 'D') this.AddValue(13, 4);\n else if (c == 'E') this.AddValue(14, 4);\n else if (c == 'F') this.AddValue(15, 4);\n }\n }\n\n Read(num_bits: number): number {\n // Read and remove items from the front of the list of bits\n if (this.data.length < num_bits) return 0;\n let result = 0;\n for (let i = 0; i < num_bits; i++) {\n result = result << 1;\n result = result + this.data[0];\n this.data.shift(); // remove the first item from the array\n }\n return result;\n }\n\n ToArray(): Buffer {\n const num_bytes = Math.ceil(this.data.length / 8.0);\n const array = Buffer.alloc(num_bytes);\n let ptr = 0;\n let shift = 7;\n for (let i = 0; i < this.data.length; i++) {\n array[ptr] += (this.data[i] << shift);\n if (shift == 0) {\n shift = 7;\n ptr++;\n }\n else {\n shift--;\n }\n }\n\n return array;\n }\n}\n"]} \ No newline at end of file +{"version":3,"file":"util.js","sourceRoot":"","sources":["../lib/util.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"crypto\";\r\n\r\nexport interface RTPPacket {\r\n id: number;\r\n timestamp: number;\r\n marker: number;\r\n padding: number;\r\n hasExtensions: number;\r\n\r\n payload: Buffer;\r\n\r\n length: number;\r\n paddingLength: number;\r\n\r\n payloadType: number;\r\n\r\n // Additional information added to the Packet\r\n wallclockTime?: Date;\r\n}\r\n\r\nexport function parseRTPPacket(buffer: Buffer): RTPPacket {\r\n const padding = (buffer[0] >> 5) & 0x01\r\n let paddingLength = 0;\r\n if (padding == 1) {\r\n // padding size is the last byte of the RTP data\r\n paddingLength = buffer[buffer.length - 1];\r\n }\r\n const hasExtensions = (buffer[0] >> 4) & 0x01;\r\n const marker = (buffer[1]) >>> 7;\r\n const payloadType = buffer[1] & 0x7f;\r\n const num_csrc_identifiers = (buffer[0] & 0x0F);\r\n\r\n const payload = buffer.slice((num_csrc_identifiers * 4) + (hasExtensions ? 16 : 12)); // includes padding\r\n const length = payload.length;\r\n\r\n\r\n return {\r\n id: buffer.readUInt16BE(2),\r\n timestamp: buffer.readUInt32BE(4),\r\n marker,\r\n padding,\r\n payloadType,\r\n hasExtensions,\r\n payload,\r\n length,\r\n paddingLength,\r\n };\r\n}\r\n\r\nexport interface SenderReport {\r\n ntpTimestampMSW: number;\r\n ntpTimestampLSW: number;\r\n rtpTimestamp: number;\r\n senderPacketCount: number;\r\n senderOctetCount: number;\r\n}\r\n\r\nexport interface RTCPPacket {\r\n buffer: Buffer;\r\n version: number;\r\n padding: number;\r\n receptionReportCount: number;\r\n packetType: number;\r\n length: number;\r\n ssrc: number;\r\n\r\n senderReport?: SenderReport;\r\n}\r\n\r\nexport function parseRTCPPacket(buffer: Buffer): RTCPPacket {\r\n\r\n // Packet Types\r\n // SR Sender Report 200\r\n // RR Receiver Report 201\r\n // SDES Source Description 202\r\n // BYE Goodbye 203\r\n // APP Application-Defined 204\r\n // RTPFB Generic RTP feedback 205\r\n // PSFB Payload-specific feedback 206\r\n // XR RTCP Extension 207\r\n const version = (buffer[0] >> 6);\r\n const padding = (buffer[0] >> 5) & 0x01;\r\n const receptionReportCount = (buffer[0]) & 0x1F;\r\n const packetType = buffer[1];\r\n const length = buffer[2] << 8 + buffer[3]; // The length in 32 bit words (not the length in bytes)\r\n const ssrc = buffer[4] << 24 + buffer[5] << 16 + buffer[6] << 8 + buffer[7];\r\n\r\n let result: RTCPPacket = {\r\n buffer,\r\n version,\r\n padding,\r\n length,\r\n ssrc,\r\n receptionReportCount,\r\n packetType};\r\n\r\n if (packetType == 200) {\r\n let senderReport: SenderReport = {\r\n ntpTimestampMSW: buffer.readUInt32BE(8),\r\n ntpTimestampLSW: buffer.readUInt32BE(12),\r\n rtpTimestamp: buffer.readUInt32BE(16),\r\n senderPacketCount: buffer.readUInt32BE(20),\r\n senderOctetCount: buffer.readUInt32BE(24)\r\n };\r\n\r\n result.senderReport = senderReport;\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// utility function for using crypto library\r\nexport function getMD5Hash(str: string): string {\r\n const md5 = createHash(\"md5\");\r\n md5.update(str);\r\n\r\n return md5.digest(\"hex\");\r\n}\r\n\r\nexport function getSHA256Hash(str: string): string {\r\n const sha256 = createHash(\"sha256\"); // use getHashes() to see what is supported\r\n sha256.update(str);\r\n\r\n return sha256.digest(\"hex\");\r\n}\r\n\r\nexport interface Transport {\r\n protocol: string;\r\n parameters: { [key: string]: string };\r\n}\r\n\r\nexport function parseTransport(transport: string): Transport {\r\n const parameters: { [key: string]: string } = {};\r\n\r\n const parts = transport.split(\";\");\r\n const protocol = parts[0];\r\n\r\n for (let i = 0; i < parts.length; i++) {\r\n const part = parts[i];\r\n const index = part.indexOf(\"=\");\r\n\r\n if (index > -1 && index !== part.length - 1) {\r\n parameters[part.substring(0, index)] = part.substring(index + 1);\r\n }\r\n }\r\n\r\n return {\r\n protocol,\r\n parameters\r\n };\r\n}\r\n\r\nexport function randInclusive(min: number, max: number): number {\r\n min = Math.ceil(min);\r\n max = Math.floor(max);\r\n return Math.floor(Math.random() * (max - min + 1)) + min;\r\n}\r\n\r\nexport function generateSSRC(): number {\r\n return randInclusive(1, 0xffffffff);\r\n}\r\n\r\n// BitStream classes by 2018 Roger Hardiman, RJH Technical Consultancy Ltd\r\n// Write to a bitstream and read back as an array\r\n\r\nexport class BitStream {\r\n\r\n data: number[] = []; // Array only stores 0 or 1 (one 'bit' per buffer item)\r\n // not very efficient on memory\r\n\r\n // Constructor\r\n // constructor() {}\r\n \r\n // Functions\r\n AddValue(value: number, num_bits: number): void {\r\n // Add each bit to the List\r\n for (let i = num_bits - 1; i >= 0; i--) {\r\n this.data.push((value >> i) & 0x01);\r\n }\r\n }\r\n\r\n AddHexString(hex_string: string): void {\r\n const hex_chars = hex_string.toUpperCase();\r\n\r\n for (let x = 0; x < hex_chars.length; x++) {\r\n const c = hex_chars.charAt(x);\r\n if (c == '0') this.AddValue(0, 4);\r\n else if (c == '1') this.AddValue(1, 4);\r\n else if (c == '2') this.AddValue(2, 4);\r\n else if (c == '3') this.AddValue(3, 4);\r\n else if (c == '4') this.AddValue(4, 4);\r\n else if (c == '5') this.AddValue(5, 4);\r\n else if (c == '6') this.AddValue(6, 4);\r\n else if (c == '7') this.AddValue(7, 4);\r\n else if (c == '8') this.AddValue(8, 4);\r\n else if (c == '9') this.AddValue(9, 4);\r\n else if (c == 'A') this.AddValue(10, 4);\r\n else if (c == 'B') this.AddValue(11, 4);\r\n else if (c == 'C') this.AddValue(12, 4);\r\n else if (c == 'D') this.AddValue(13, 4);\r\n else if (c == 'E') this.AddValue(14, 4);\r\n else if (c == 'F') this.AddValue(15, 4);\r\n }\r\n }\r\n\r\n Read(num_bits: number): number {\r\n // Read and remove items from the front of the list of bits\r\n if (this.data.length < num_bits) return 0;\r\n let result = 0;\r\n for (let i = 0; i < num_bits; i++) {\r\n result = result << 1;\r\n result = result + this.data[0];\r\n this.data.shift(); // remove the first item from the array\r\n }\r\n return result;\r\n }\r\n\r\n ToArray(): Buffer {\r\n const num_bytes = Math.ceil(this.data.length / 8.0);\r\n const array = Buffer.alloc(num_bytes);\r\n let ptr = 0;\r\n let shift = 7;\r\n for (let i = 0; i < this.data.length; i++) {\r\n array[ptr] += (this.data[i] << shift);\r\n if (shift == 0) {\r\n shift = 7;\r\n ptr++;\r\n }\r\n else {\r\n shift--;\r\n }\r\n }\r\n\r\n return array;\r\n }\r\n}\r\n"]} \ No newline at end of file diff --git a/package.json b/package.json index 2f6c2db..68159c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yellowstone", - "version": "3.0.7", + "version": "3.0.8", "description": "An RTSP client for node.js.", "main": "dist/index.js", "repository": {