Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions examples/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ program.description("Yellowstone RTSP Client Test Software");
program.option('-u, --username <value>', 'Optional RTSP Username');
program.option('-p, --password <value>', 'Optional RTSP Password');
program.option('-o, --outfile <value>', 'Optional Output File with no File Extension for captured H264/H265/AV1/AAC');
program.option('-t, --transport <value>', 'Optional RTP Transport - UDP or TCP');

program.argument('<rtsp url eg rtsp://1.2.3.4/stream1>');

Expand All @@ -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);

Expand All @@ -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");

Expand Down
32 changes: 28 additions & 4 deletions lib/RTSPClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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."
);
}

Expand Down Expand Up @@ -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") {
Expand Down
Loading