remove everything related to rover mic while keeping tts

This commit is contained in:
legop3
2025-11-27 23:15:21 -05:00
parent 509dbd870a
commit 5f5591ca42
11 changed files with 8 additions and 173 deletions
Vendored
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+3
View File
@@ -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)
+1 -4
View File
@@ -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
View File
@@ -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,
+2 -5
View File
@@ -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
} }
+1 -1
View File
@@ -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
-2
View File
@@ -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"
-1
View File
@@ -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');
-145
View File
@@ -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,
};
-14
View File
@@ -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) {