mirror of https://github.com/jetkvm/kvm.git
fix: handle audio track recreation to prevent microphone failures
When browsers recreate audio tracks during long sessions, the backend now properly handles the new track instead of ignoring it. Old track handlers detect when they've been superseded and exit cleanly to prevent goroutine leaks.
This commit is contained in:
parent
3448663afa
commit
ce4ea10551
28
audio.go
28
audio.go
|
|
@ -21,7 +21,7 @@ var (
|
||||||
activeConnections atomic.Int32
|
activeConnections atomic.Int32
|
||||||
audioLogger zerolog.Logger
|
audioLogger zerolog.Logger
|
||||||
currentAudioTrack *webrtc.TrackLocalStaticSample
|
currentAudioTrack *webrtc.TrackLocalStaticSample
|
||||||
inputTrackHandling atomic.Bool
|
currentInputTrack atomic.Pointer[string]
|
||||||
audioOutputEnabled atomic.Bool
|
audioOutputEnabled atomic.Bool
|
||||||
audioInputEnabled atomic.Bool
|
audioInputEnabled atomic.Bool
|
||||||
)
|
)
|
||||||
|
|
@ -152,14 +152,10 @@ func setAudioTrack(audioTrack *webrtc.TrackLocalStaticSample) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPendingInputTrack(track *webrtc.TrackRemote) {
|
func setPendingInputTrack(track *webrtc.TrackRemote) {
|
||||||
audioMutex.Lock()
|
trackID := track.ID()
|
||||||
defer audioMutex.Unlock()
|
currentInputTrack.Store(&trackID)
|
||||||
|
|
||||||
// Start input track handler only once per WebRTC session
|
|
||||||
if inputTrackHandling.CompareAndSwap(false, true) {
|
|
||||||
go handleInputTrackForSession(track)
|
go handleInputTrackForSession(track)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// SetAudioOutputEnabled enables or disables audio output
|
// SetAudioOutputEnabled enables or disables audio output
|
||||||
func SetAudioOutputEnabled(enabled bool) error {
|
func SetAudioOutputEnabled(enabled bool) error {
|
||||||
|
|
@ -202,22 +198,32 @@ func SetAudioInputEnabled(enabled bool) error {
|
||||||
// handleInputTrackForSession runs for the entire WebRTC session lifetime
|
// handleInputTrackForSession runs for the entire WebRTC session lifetime
|
||||||
// It continuously reads from the track and sends to whatever relay is currently active
|
// It continuously reads from the track and sends to whatever relay is currently active
|
||||||
func handleInputTrackForSession(track *webrtc.TrackRemote) {
|
func handleInputTrackForSession(track *webrtc.TrackRemote) {
|
||||||
defer inputTrackHandling.Store(false)
|
myTrackID := track.ID()
|
||||||
|
|
||||||
audioLogger.Debug().
|
audioLogger.Debug().
|
||||||
Str("codec", track.Codec().MimeType).
|
Str("codec", track.Codec().MimeType).
|
||||||
Str("track_id", track.ID()).
|
Str("track_id", myTrackID).
|
||||||
Msg("starting session-lifetime track handler")
|
Msg("starting session-lifetime track handler")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
// Check if we've been superseded by a new track
|
||||||
|
currentTrackID := currentInputTrack.Load()
|
||||||
|
if currentTrackID != nil && *currentTrackID != myTrackID {
|
||||||
|
audioLogger.Debug().
|
||||||
|
Str("my_track_id", myTrackID).
|
||||||
|
Str("current_track_id", *currentTrackID).
|
||||||
|
Msg("audio track handler exiting - superseded by new track")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Read RTP packet (must always read to keep track alive)
|
// Read RTP packet (must always read to keep track alive)
|
||||||
rtpPacket, _, err := track.ReadRTP()
|
rtpPacket, _, err := track.ReadRTP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
audioLogger.Debug().Msg("audio track ended")
|
audioLogger.Debug().Str("track_id", myTrackID).Msg("audio track ended")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
audioLogger.Warn().Err(err).Msg("failed to read RTP packet")
|
audioLogger.Warn().Err(err).Str("track_id", myTrackID).Msg("failed to read RTP packet")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue