From 7e62c2fc651f71613cc0147c0c4d81b2dabe1291 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Fri, 27 Feb 2026 13:32:08 +0100 Subject: [PATCH] Voice fix: clone TTS track to prevent Teams from killing it via track.stop() Made-with: Cursor --- src/bot/audioProcedure.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/bot/audioProcedure.ts b/src/bot/audioProcedure.ts index d37459f..b301482 100644 --- a/src/bot/audioProcedure.ts +++ b/src/bot/audioProcedure.ts @@ -62,8 +62,8 @@ export class AudioProcedure { // Build a new stream: our TTS audio track + their video tracks const combinedStream = new MediaStream(); - // Add our controlled audio track (TTS will be piped here) - streamDest.stream.getAudioTracks().forEach(t => combinedStream.addTrack(t)); + // Clone the TTS track so Teams can't kill the original via track.stop() + streamDest.stream.getAudioTracks().forEach(t => combinedStream.addTrack(t.clone())); // Keep the real video tracks (from fake camera) realStream.getVideoTracks().forEach(t => combinedStream.addTrack(t)); @@ -114,14 +114,15 @@ export class AudioProcedure { // #region agent log diag.beforeSenderTrackIds.push(sender.track.id); // #endregion - await sender.replaceTrack(ttsTrack); + const freshClone = ttsTrack.clone(); + await sender.replaceTrack(freshClone); replaced++; // #region agent log const afterTrack = sender.track; diag.afterSenderTrackIds.push(afterTrack?.id || 'null'); diag.afterSenderTrackEnabled = afterTrack?.enabled; diag.afterSenderTrackReadyState = afterTrack?.readyState; - // Force track enabled just in case + diag.originalTrackState = ttsTrack.readyState; if (afterTrack && !afterTrack.enabled) { afterTrack.enabled = true; diag.forcedEnabled = true;