Files
MultiRoombaRover/server/src/services/mediaMtxService/config.js
T

100 lines
3.6 KiB
JavaScript

// MediaMTX Config Builder
// Purpose: Converts the rover server's media settings into the complete MediaMTX runtime configuration.
// Scope: Keeps deployment-specific hosts in the configuration database while protocol policy remains application-owned.
const path = require('path');
function normalizeAdditionalHosts(rawHosts) {
if (rawHosts == null) return [];
if (!Array.isArray(rawHosts)) {
throw new Error('media.additionalHosts must be a list');
}
/*
MediaMTX accepts both IP addresses and DNS names here. Preserve that flexibility because
an installation can need a public candidate and a LAN candidate at the same time. Empty
entries and duplicates are removed so a harmless config typo does not create redundant
ICE candidates, while the values themselves remain entirely instance-owned.
*/
return [...new Set(rawHosts.map((value) => String(value || '').trim()).filter(Boolean))];
}
function buildMediaMtxConfig({ config, serverPort, snapshotWriterPath }) {
const media = config?.media || {};
const webrtcPort = media.webrtcPort ?? 8189;
const configuredHosts = normalizeAdditionalHosts(media.additionalHosts);
let publicHostname = '';
try {
publicHostname = new URL(config?.publicUrl).hostname;
} catch {
// The assembled configuration schema normally prevents this. Keeping the
// builder tolerant makes its pure tests and startup error path explicit.
}
// The canonical public hostname is always advertised once. Operators only
// maintain genuinely additional LAN names, aliases, or fixed IP addresses.
const additionalHosts = [...new Set([publicHostname, ...configuredHosts].filter(Boolean))];
const authPort = Number(serverPort) || 8080;
return {
logLevel: 'info',
api: true,
apiAddress: '127.0.0.1:9997',
metrics: true,
metricsAddress: '127.0.0.1:9998',
pprof: false,
pprofAddress: '127.0.0.1:9999',
/*
Rover publishers and readers always request TCP explicitly. Declaring only TCP here
also prevents MediaMTX from opening the separate RTP/RTCP UDP listeners, which are not
useful for this local-network deployment and performed poorly in the measured tests.
*/
rtsp: true,
rtspAddress: ':8554',
rtspTransports: ['tcp'],
rtmp: false,
hls: false,
webrtc: true,
// WHEP and WHIP signaling is public only through the Node /video proxy.
// The configured ICE transport port remains directly reachable by browsers.
webrtcAddress: '127.0.0.1:8889',
webrtcLocalUDPAddress: `:${webrtcPort}`,
webrtcLocalTCPAddress: `:${webrtcPort}`,
webrtcAdditionalHosts: additionalHosts,
webrtcICEServers2: [
{ url: 'stun:stun.l.google.com:19302' },
{ url: 'stun:stun1.l.google.com:19302' },
{ url: 'stun:stun2.l.google.com:19302' },
{ url: 'stun:stun3.l.google.com:19302' },
{ url: 'stun:stun4.l.google.com:19302' },
{ url: 'stun:stun.cloudflare.com:3478' },
],
// Every publisher and server-local reader uses RTSP over TCP. Disable SRT
// completely so MediaMTX cannot reintroduce the GoSRT/libSRT ACKACK mismatch.
srt: false,
authMethod: 'http',
authHTTPAddress: `http://127.0.0.1:${authPort}/mediamtx/auth`,
authHTTPExclude: [
{ action: 'api' },
{ action: 'metrics' },
{ action: 'pprof' },
],
paths: {
'~^room-audio/': { source: 'publisher' },
all: {
source: 'publisher',
sourceOnDemand: false,
runOnReady: path.resolve(snapshotWriterPath),
runOnReadyRestart: true,
},
},
};
}
module.exports = {
buildMediaMtxConfig,
normalizeAdditionalHosts,
};