diff --git a/examples/demo.js b/examples/demo.js index 6b878bb..74a9de8 100644 --- a/examples/demo.js +++ b/examples/demo.js @@ -23,6 +23,7 @@ program.description("Yellowstone RTSP Client Test Software"); program.option('-u, --username ', 'Optional RTSP Username'); program.option('-p, --password ', 'Optional RTSP Password'); program.option('-o, --outfile ', 'Optional Output File with no File Extension for captured H264/H265/AV1/AAC'); +program.option('-t, --transport ', 'Optional RTP Transport - UDP or TCP'); program.argument(''); @@ -36,7 +37,10 @@ let password = ""; if ('username' in options) username = options.username; if ('password' in options) password = options.password; -const filename = "outfile" +let transport = "tcp"; +if ('transport' in options) transport = options.transport.toString().toLowerCase(); + + const filename = "outfile" console.log("Connecting to " + url); @@ -48,7 +52,7 @@ const client = new RTSPClient(username, password); // "keepAlive" option is set to true by default // "connection" option is set to "udp" by default and defines the method the RTP media packets are set to Yellowstone. Options are "udp" or "tcp" (where RTP media packets are sent down the RTSP connection) // "secure" option is set to true when connecting with TLS to the RTSP Server (eg for RTSPS) -client.connect(url, { connection: "tcp", secure: false }) +client.connect(url, { connection: transport, secure: false }) .then(async (detailsArray) => { console.log("Connected"); diff --git a/lib/RTSPClient.ts b/lib/RTSPClient.ts index 6ad039d..4c02874 100644 --- a/lib/RTSPClient.ts +++ b/lib/RTSPClient.ts @@ -496,11 +496,15 @@ export default class RTSPClient extends EventEmitter { const transport = parseTransport(headers.Transport); if ( + // TCP transport.protocol !== "RTP/AVP/TCP" && - transport.protocol !== "RTP/AVP" + // UDP + transport.protocol !== "RTP/AVP" && + // UDP + transport.protocol !== "RTP/AVP/UDP" // Panasonic cameras send this ) { throw new Error( - "Only RTSP servers supporting RTP/AVP(unicast) or RTP/AVP/TCP are supported at this time." + "Only RTSP servers supporting RTP/AVP or RTP/AVP/UDP or RTP/AVP/TCP are supported at this time." ); } @@ -904,10 +908,30 @@ export default class RTSPClient extends EventEmitter { const key = line.substring(0, indexOf).trim(); const data = line.substring(indexOf + 1).trim(); - this.rtspHeaders[key] = - key != "Session" && data.match(/^[0-9]+$/) + 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") && this.rtspHeaders[key]?.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") {