mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
remove everything related to rover mic while keeping tts
This commit is contained in:
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
@@ -38,6 +38,9 @@ else
|
|||||||
AUDIO_RATE=8000
|
AUDIO_RATE=8000
|
||||||
AUDIO_BITRATE=64000
|
AUDIO_BITRATE=64000
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Disable audio streaming for now; only video will be published.
|
||||||
|
AUDIO_ENABLE=0
|
||||||
# Flip the camera 180deg (supported by rpicam-vid/libcamera-vid)
|
# Flip the camera 180deg (supported by rpicam-vid/libcamera-vid)
|
||||||
FLIP_ARGS=(--rotation 180)
|
FLIP_ARGS=(--rotation 180)
|
||||||
|
|
||||||
|
|||||||
@@ -205,10 +205,7 @@ VIDEO_HEIGHT=720
|
|||||||
VIDEO_FPS=30
|
VIDEO_FPS=30
|
||||||
VIDEO_BITRATE=3000000
|
VIDEO_BITRATE=3000000
|
||||||
AUDIO_ENABLE=1
|
AUDIO_ENABLE=1
|
||||||
AUDIO_DEVICE=plughw:0,0
|
AUDIO_ENABLE=0
|
||||||
AUDIO_RATE=48000
|
|
||||||
AUDIO_CHANNELS=1
|
|
||||||
AUDIO_BITRATE=24000
|
|
||||||
ENV
|
ENV
|
||||||
chown roverd:roverd /var/lib/roverd/video.env
|
chown roverd:roverd /var/lib/roverd/video.env
|
||||||
chmod 0640 /var/lib/roverd/video.env
|
chmod 0640 /var/lib/roverd/video.env
|
||||||
|
|||||||
+1
-1
@@ -141,7 +141,7 @@ func LoadConfig(path string) (*Config, error) {
|
|||||||
NudgeDegrees: 2,
|
NudgeDegrees: 2,
|
||||||
},
|
},
|
||||||
Audio: AudioConfig{
|
Audio: AudioConfig{
|
||||||
CaptureEnabled: true,
|
CaptureEnabled: false,
|
||||||
CaptureDevice: "plughw:0,0",
|
CaptureDevice: "plughw:0,0",
|
||||||
SampleRate: 48000,
|
SampleRate: 48000,
|
||||||
Channels: 1,
|
Channels: 1,
|
||||||
|
|||||||
@@ -25,11 +25,8 @@ func UpdatePublisherEnv(media MediaConfig, audio AudioConfig) error {
|
|||||||
fmt.Fprintf(&buf, "VIDEO_HEIGHT=%d\n", media.VideoHeight)
|
fmt.Fprintf(&buf, "VIDEO_HEIGHT=%d\n", media.VideoHeight)
|
||||||
fmt.Fprintf(&buf, "VIDEO_FPS=%d\n", media.VideoFPS)
|
fmt.Fprintf(&buf, "VIDEO_FPS=%d\n", media.VideoFPS)
|
||||||
fmt.Fprintf(&buf, "VIDEO_BITRATE=%d\n", media.VideoBitrate)
|
fmt.Fprintf(&buf, "VIDEO_BITRATE=%d\n", media.VideoBitrate)
|
||||||
fmt.Fprintf(&buf, "AUDIO_ENABLE=%d\n", boolToInt(audio.CaptureEnabled))
|
// Disable mic streaming; audio is handled locally for TTS only.
|
||||||
fmt.Fprintf(&buf, "AUDIO_DEVICE=%s\n", audio.CaptureDevice)
|
fmt.Fprintf(&buf, "AUDIO_ENABLE=0\n")
|
||||||
fmt.Fprintf(&buf, "AUDIO_RATE=%d\n", audio.SampleRate)
|
|
||||||
fmt.Fprintf(&buf, "AUDIO_CHANNELS=%d\n", audio.Channels)
|
|
||||||
fmt.Fprintf(&buf, "AUDIO_BITRATE=%d\n", audio.Bitrate)
|
|
||||||
if err := os.WriteFile(publisherEnvPath, buf.Bytes(), 0o640); err != nil {
|
if err := os.WriteFile(publisherEnvPath, buf.Bytes(), 0o640); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ cameraServo:
|
|||||||
nudgeDegrees: 2
|
nudgeDegrees: 2
|
||||||
allowRawPulse: false
|
allowRawPulse: false
|
||||||
audio:
|
audio:
|
||||||
captureEnabled: true
|
captureEnabled: false
|
||||||
captureDevice: plughw:0,0
|
captureDevice: plughw:0,0
|
||||||
sampleRate: 48000
|
sampleRate: 48000
|
||||||
channels: 1
|
channels: 1
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ media:
|
|||||||
# http://<base>/<roverId>/whep
|
# http://<base>/<roverId>/whep
|
||||||
# Example: http://192.168.0.86:8889/video
|
# Example: http://192.168.0.86:8889/video
|
||||||
whepBaseUrl: "http://192.168.0.86:8889/video"
|
whepBaseUrl: "http://192.168.0.86:8889/video"
|
||||||
# Set audioBridge to false to disable server-side audio transcoding from <id>-raw -> <id>
|
|
||||||
# audioBridge: true
|
|
||||||
|
|
||||||
homeAssistant:
|
homeAssistant:
|
||||||
url: "http://homeassistant.local:8123"
|
url: "http://homeassistant.local:8123"
|
||||||
|
|||||||
@@ -26,4 +26,3 @@ require('./src/services/sessionService');
|
|||||||
require('./src/services/batteryManager');
|
require('./src/services/batteryManager');
|
||||||
require('./src/services/discordBotService');
|
require('./src/services/discordBotService');
|
||||||
require('./src/services/httpServer');
|
require('./src/services/httpServer');
|
||||||
require('./src/services/audioBridgeService');
|
|
||||||
|
|||||||
@@ -1,145 +0,0 @@
|
|||||||
const { spawn } = require('child_process');
|
|
||||||
const logger = require('../globals/logger').child('audioBridge');
|
|
||||||
const { loadConfig } = require('../helpers/configLoader');
|
|
||||||
|
|
||||||
const config = loadConfig();
|
|
||||||
|
|
||||||
const MEDIAMTX_API =
|
|
||||||
process.env.MEDIAMTX_API || 'http://127.0.0.1:9997/v3/paths/list';
|
|
||||||
const SRT_HOST = process.env.MEDIAMTX_SRT_HOST || '127.0.0.1';
|
|
||||||
const POLL_MS = 5000;
|
|
||||||
|
|
||||||
// Track active ffmpeg bridges keyed by raw path name (e.g., rover-raw).
|
|
||||||
const bridges = new Map();
|
|
||||||
|
|
||||||
function stopBridge(rawName) {
|
|
||||||
const proc = bridges.get(rawName);
|
|
||||||
if (!proc) return;
|
|
||||||
bridges.delete(rawName);
|
|
||||||
proc.kill('SIGTERM');
|
|
||||||
logger.info('stopped bridge', { rawName });
|
|
||||||
}
|
|
||||||
|
|
||||||
function startBridge(rawName, baseName) {
|
|
||||||
if (bridges.has(rawName)) return;
|
|
||||||
|
|
||||||
// Use explicit credentials so mediamtx auth will allow the bridge to pull.
|
|
||||||
const inputUrl = `srt://${SRT_HOST}:9000?streamid=#!::r=${rawName},m=request&mode=caller&transtype=live&latency=20&u=bridge&p=bridge`;
|
|
||||||
const outputUrl = `srt://${SRT_HOST}:9000?streamid=#!::r=${baseName},m=publish&mode=caller&transtype=live&pkt_size=1316`;
|
|
||||||
|
|
||||||
const args = [
|
|
||||||
'-hide_banner',
|
|
||||||
'-loglevel',
|
|
||||||
'warning',
|
|
||||||
'-fflags',
|
|
||||||
'nobuffer',
|
|
||||||
'-thread_queue_size',
|
|
||||||
'512',
|
|
||||||
'-i',
|
|
||||||
inputUrl,
|
|
||||||
'-map',
|
|
||||||
'0:v',
|
|
||||||
'-map',
|
|
||||||
'0:a?',
|
|
||||||
'-c:v',
|
|
||||||
'copy',
|
|
||||||
'-c:a',
|
|
||||||
'libopus',
|
|
||||||
'-b:a',
|
|
||||||
'24000',
|
|
||||||
'-ac',
|
|
||||||
'1',
|
|
||||||
'-ar',
|
|
||||||
'16000',
|
|
||||||
'-application',
|
|
||||||
'voip',
|
|
||||||
'-frame_duration',
|
|
||||||
'60',
|
|
||||||
'-af',
|
|
||||||
'pan=1c|c0=c0,volume=12dB',
|
|
||||||
'-flush_packets',
|
|
||||||
'1',
|
|
||||||
'-f',
|
|
||||||
'mpegts',
|
|
||||||
outputUrl,
|
|
||||||
];
|
|
||||||
|
|
||||||
const proc = spawn('ffmpeg', args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
||||||
bridges.set(rawName, proc);
|
|
||||||
logger.info('started bridge', { rawName, baseName });
|
|
||||||
|
|
||||||
proc.stdout.on('data', (data) => {
|
|
||||||
logger.info(data.toString().trim());
|
|
||||||
});
|
|
||||||
proc.stderr.on('data', (data) => {
|
|
||||||
logger.info(data.toString().trim());
|
|
||||||
});
|
|
||||||
proc.on('exit', (code, signal) => {
|
|
||||||
bridges.delete(rawName);
|
|
||||||
logger.info('bridge exited', { rawName, code, signal });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchPaths() {
|
|
||||||
try {
|
|
||||||
const res = await fetch(MEDIAMTX_API, { signal: AbortSignal.timeout(3000) });
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(`mediamtx api ${res.status}`);
|
|
||||||
}
|
|
||||||
return res.json();
|
|
||||||
} catch (err) {
|
|
||||||
logger.warn('path fetch failed: %s', err.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function pickRawPairs(items) {
|
|
||||||
const names = new Set(items.map((i) => i.name));
|
|
||||||
return items
|
|
||||||
.filter((i) => i.name.endsWith('-raw'))
|
|
||||||
.map((raw) => {
|
|
||||||
const baseName = raw.name.slice(0, -4);
|
|
||||||
return { rawName: raw.name, baseName };
|
|
||||||
})
|
|
||||||
.filter(({ baseName }) => !names.has(baseName));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function reconcile() {
|
|
||||||
const data = await fetchPaths();
|
|
||||||
if (!data?.items) {
|
|
||||||
// On failure, stop all bridges so we don't run blind.
|
|
||||||
Array.from(bridges.keys()).forEach(stopBridge);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeRaw = new Set();
|
|
||||||
pickRawPairs(data.items).forEach(({ rawName, baseName }) => {
|
|
||||||
activeRaw.add(rawName);
|
|
||||||
startBridge(rawName, baseName);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Stop bridges whose raw path disappeared.
|
|
||||||
Array.from(bridges.keys()).forEach((rawName) => {
|
|
||||||
if (!activeRaw.has(rawName)) {
|
|
||||||
stopBridge(rawName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
// Only enable when explicit media config allows it, or by default.
|
|
||||||
const enabled = config.media?.audioBridge !== false;
|
|
||||||
if (!enabled) {
|
|
||||||
logger.info('audio bridge disabled by config');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.info('audio bridge enabled; watching for *-raw streams');
|
|
||||||
reconcile();
|
|
||||||
setInterval(reconcile, POLL_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
start,
|
|
||||||
};
|
|
||||||
@@ -71,23 +71,9 @@ function canView(socket) {
|
|||||||
app.post('/mediamtx/auth', (req, res) => {
|
app.post('/mediamtx/auth', (req, res) => {
|
||||||
const body = req.body || {};
|
const body = req.body || {};
|
||||||
const path = (body.path || '').replace(/^\//, '');
|
const path = (body.path || '').replace(/^\//, '');
|
||||||
const password = body.pass || body.password || '';
|
|
||||||
const action = body.action || '';
|
|
||||||
const sessionId = body.user;
|
const sessionId = body.user;
|
||||||
const streamInfo = extractStreamInfo(path);
|
const streamInfo = extractStreamInfo(path);
|
||||||
|
|
||||||
// Allow internal bridge (server-side audio transcode) to pull/push without a websocket session.
|
|
||||||
if (body.user === 'bridge' && password === 'bridge') {
|
|
||||||
return res.status(200).end();
|
|
||||||
}
|
|
||||||
// Also allow localhost reads of *-raw paths for the bridge even if creds are missing.
|
|
||||||
if (
|
|
||||||
streamInfo?.id?.endsWith('-raw') &&
|
|
||||||
(req.ip === '127.0.0.1' || req.ip === '::1')
|
|
||||||
) {
|
|
||||||
return res.status(200).end();
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('video auth request', { path: body.path, sessionId, stream: streamInfo });
|
logger.info('video auth request', { path: body.path, sessionId, stream: streamInfo });
|
||||||
|
|
||||||
if (!sessionId || !streamInfo?.id) {
|
if (!sessionId || !streamInfo?.id) {
|
||||||
|
|||||||
Reference in New Issue
Block a user