mirror of
https://github.com/koush/scrypted.git
synced 2026-03-06 03:12:00 +00:00
common: fix rtsp track selection, path suffixing, udp/tcp sdp port setup.
This commit is contained in:
@@ -14,10 +14,6 @@ interface Headers {
|
||||
[header: string]: string
|
||||
}
|
||||
|
||||
function findSyncFrame(streamChunks: StreamChunk[]): StreamChunk[] {
|
||||
return streamChunks;
|
||||
}
|
||||
|
||||
export interface RtspStreamParser extends StreamParser {
|
||||
sdp: Promise<string>;
|
||||
}
|
||||
@@ -141,14 +137,16 @@ export class RtspClient extends RtspBase {
|
||||
}
|
||||
}
|
||||
|
||||
writeRequest(method: string, headers?: Headers, path?: string, body?: Buffer, authenticating?: boolean) {
|
||||
writeRequest(method: string, headers?: Headers, path?: string, body?: Buffer) {
|
||||
headers = headers || {};
|
||||
|
||||
let fullUrl: string;
|
||||
if (path)
|
||||
fullUrl = new URL(path, this.url).toString();
|
||||
else
|
||||
fullUrl = this.url;
|
||||
let fullUrl = this.url;
|
||||
if (path) {
|
||||
// strangely, RTSP urls do not behave like expected from an HTTP-ish server.
|
||||
// ffmpeg will happily suffix path segments after query strings:
|
||||
// SETUP rtsp://localhost:5554/cam/realmonitor?channel=1&subtype=0/trackID=0 RTSP/1.0
|
||||
fullUrl += '/' + path;
|
||||
}
|
||||
|
||||
const sanitized = new URL(fullUrl);
|
||||
sanitized.username = '';
|
||||
@@ -172,7 +170,7 @@ export class RtspClient extends RtspBase {
|
||||
headers: Headers,
|
||||
body: Buffer
|
||||
}> {
|
||||
this.writeRequest(method, headers, path, body, authenticating);
|
||||
this.writeRequest(method, headers, path, body);
|
||||
|
||||
const message = await this.readMessage();
|
||||
const status = message[0];
|
||||
@@ -381,6 +379,8 @@ export class RtspServer {
|
||||
this.respond(200, 'OK', requestHeaders, headers, Buffer.from(this.sdp))
|
||||
}
|
||||
|
||||
// todo: use the sdp itself to determine the audio/video track ids so
|
||||
// rewriting is not necessary.
|
||||
setup(url: string, requestHeaders: Headers) {
|
||||
const headers: Headers = {};
|
||||
const transport = requestHeaders['transport'];
|
||||
@@ -391,7 +391,7 @@ export class RtspServer {
|
||||
const [_, rtp, rtcp] = match;
|
||||
if (url.includes('audio'))
|
||||
this.udpPorts.audio = parseInt(rtp);
|
||||
else
|
||||
else if (url.includes('video'))
|
||||
this.udpPorts.video = parseInt(rtp);
|
||||
}
|
||||
else if (transport.includes('TCP')) {
|
||||
@@ -402,7 +402,7 @@ export class RtspServer {
|
||||
if (url.includes('audio')) {
|
||||
this.audioChannel = low;
|
||||
}
|
||||
else {
|
||||
else if (url.includes('video')) {
|
||||
this.videoChannel = low;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,33 @@ export function parsePayloadTypes(sdp: string) {
|
||||
|
||||
export function findTrack(sdp: string, type: string, directions: TrackDirection[] = ['recvonly']) {
|
||||
const tracks = sdp.split('m=').filter(track => track.startsWith(type));
|
||||
|
||||
for (const track of tracks) {
|
||||
const returnTrack = () => {
|
||||
const lines = track.split('\n').map(line => line.trim());
|
||||
const control = lines.find(line => line.startsWith('a=control:'));
|
||||
return {
|
||||
section: 'm=' + track,
|
||||
trackId: control?.split('a=control:')?.[1],
|
||||
};
|
||||
}
|
||||
|
||||
for (const dir of directions) {
|
||||
if (track.includes(`a=${dir}`)) {
|
||||
const lines = track.split('\n').map(line => line.trim());
|
||||
const control = lines.find(line => line.startsWith('a=control:'));
|
||||
return {
|
||||
section: 'm=' + track,
|
||||
trackId: control?.split('a=control:')?.[1],
|
||||
};
|
||||
return returnTrack();
|
||||
}
|
||||
}
|
||||
|
||||
// some sdp do not advertise a media flow direction. i think recvonly is the default?
|
||||
if ((directions.includes('recvonly'))
|
||||
&& !track.includes('sendonly')
|
||||
&& !track.includes('inactive')) {
|
||||
return returnTrack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TrackDirection = 'sendonly' | 'sendrecv' | 'recvonly';
|
||||
type TrackDirection = 'sendonly' | 'sendrecv' | 'recvonly' | 'inactive';
|
||||
|
||||
export function parseTrackIds(sdp: string, directions: TrackDirection[] = ['recvonly', 'sendrecv']) {
|
||||
return {
|
||||
|
||||
@@ -10,10 +10,15 @@ import { RTCSessionControl, ScryptedDeviceBase } from "@scrypted/sdk";
|
||||
// h264 baseline and opus are required codecs that all webrtc implementations must provide.
|
||||
function createSdpInput(audioPort: number, videoPort: number, sdp: string) {
|
||||
let outputSdp = sdp
|
||||
.replace(/c=IN .*?/, `c=IN IP4 127.0.0.1`)
|
||||
.replace(/m=audio \d+/, `m=audio ${audioPort}`)
|
||||
.replace(/m=video \d+/, `m=video ${videoPort}`);
|
||||
|
||||
const lines = outputSdp.split('\n').map(line => line.trim());
|
||||
let lines = outputSdp.split('\n').map(line => line.trim());
|
||||
lines = lines
|
||||
.filter(line => !line.includes('a=candidate'))
|
||||
.filter(line => !line.includes('a=ice'));
|
||||
|
||||
const vindex = lines.findIndex(line => line.startsWith('m=video'));
|
||||
lines.splice(vindex + 1, 0, 'a=control:trackID=video');
|
||||
const aindex = lines.findIndex(line => line.startsWith('m=audio'));
|
||||
@@ -43,8 +48,8 @@ export function getRTCMediaStreamOptions(id: string, name: string): MediaStreamO
|
||||
|
||||
export async function createRTCPeerConnectionSource(channel: ScryptedDeviceBase & RTCSignalingChannel, id: string): Promise<FFMpegInput> {
|
||||
const { console, name } = channel;
|
||||
const videoPort = Math.round(Math.random() * 10000 + 30000);
|
||||
const audioPort = Math.round(Math.random() * 10000 + 30000);
|
||||
const videoPort = useUdp ? Math.round(Math.random() * 10000 + 30000) : 0;
|
||||
const audioPort = useUdp ? Math.round(Math.random() * 10000 + 30000) : 0;
|
||||
|
||||
const { clientPromise, port } = await listenZeroSingleClient();
|
||||
|
||||
@@ -65,13 +70,13 @@ export async function createRTCPeerConnectionSource(channel: ScryptedDeviceBase
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
sessionControl?.endSession().catch(() => {});
|
||||
sessionControl?.endSession().catch(() => { });
|
||||
};
|
||||
|
||||
clientPromise.then(async (client) => {
|
||||
socket = client;
|
||||
const rtspServer = new RtspServer(socket, undefined, udp);
|
||||
// rtspServer.console = console;
|
||||
rtspServer.console = console;
|
||||
rtspServer.audioChannel = 0;
|
||||
rtspServer.videoChannel = 2;
|
||||
|
||||
@@ -203,7 +208,7 @@ export async function createRTCPeerConnectionSource(channel: ScryptedDeviceBase
|
||||
};
|
||||
rtspServer.client.write(rtspServer.sdp + '\r\n');
|
||||
rtspServer.client.end();
|
||||
rtspServer.client.on('data', () => {});
|
||||
rtspServer.client.on('data', () => { });
|
||||
// rtspServer.client.destroy();
|
||||
console.log('sdp sent');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user