mirror of
https://github.com/luckfox-eng29/kvm.git
synced 2026-01-17 19:22:15 +01:00
174 lines
4.2 KiB
Go
174 lines
4.2 KiB
Go
package kvm
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/pion/rtp"
|
|
)
|
|
|
|
var (
|
|
audioListener *net.UDPConn
|
|
currentPort int
|
|
mutex sync.Mutex
|
|
portList = []int{3333}
|
|
portIndex = 0
|
|
)
|
|
|
|
const (
|
|
maxAudioFrameSize = 1500
|
|
frameDurationMs = 20
|
|
timestampRate = 48000
|
|
timestampStep = timestampRate * frameDurationMs / 1000
|
|
)
|
|
|
|
func waitUDPPortReleased(port int, retries int, delay time.Duration) error {
|
|
addr := fmt.Sprintf("127.0.0.1:%d", port)
|
|
for i := 0; i < retries; i++ {
|
|
conn, err := net.ListenPacket("udp", addr)
|
|
if err == nil {
|
|
conn.Close()
|
|
return nil
|
|
}
|
|
time.Sleep(delay)
|
|
}
|
|
return fmt.Errorf("port %d still in use after %d retries", port, retries)
|
|
}
|
|
|
|
func getNextAvailablePort() int {
|
|
for i := 0; i < len(portList); i++ {
|
|
port := portList[portIndex]
|
|
portIndex = (portIndex + 1) % len(portList)
|
|
addr := fmt.Sprintf("127.0.0.1:%d", port)
|
|
conn, err := net.ListenPacket("udp", addr)
|
|
if err == nil {
|
|
conn.Close()
|
|
return port
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func StartNtpAudioServer(handleClient func(net.Conn)) {
|
|
mutex.Lock()
|
|
defer mutex.Unlock()
|
|
|
|
if audioListener != nil || lastAudioState.Ready {
|
|
StopNtpAudioServer()
|
|
}
|
|
|
|
port := getNextAvailablePort()
|
|
if port == 0 {
|
|
audioLogger.Error().Msg("no available ports to start audio server")
|
|
return
|
|
}
|
|
|
|
listener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: port})
|
|
if err != nil {
|
|
audioLogger.Error().Err(err).Msg("failed to start server on port %d")
|
|
return
|
|
}
|
|
|
|
audioListener = listener
|
|
currentPort = port
|
|
|
|
if config.AudioMode == "usb" {
|
|
_, err := CallAudioCtrlAction("set_audio_mode", map[string]interface{}{"audio_mode": "usb", "rtp_port": port})
|
|
if err != nil {
|
|
audioLogger.Error().Err(err).Msg("failed to set audio mode")
|
|
}
|
|
_, err = CallAudioCtrlAction("set_audio_enable", map[string]interface{}{"audio_enable": true})
|
|
if err != nil {
|
|
audioLogger.Error().Err(err).Msg("failed to set audio enable")
|
|
}
|
|
} else {
|
|
_, err := CallAudioCtrlAction("set_audio_mode", map[string]interface{}{"audio_mode": "hdmi", "rtp_port": port})
|
|
if err != nil {
|
|
audioLogger.Error().Err(err).Msg("failed to set audio mode")
|
|
}
|
|
_, err = CallAudioCtrlAction("set_audio_enable", map[string]interface{}{"audio_enable": true})
|
|
if err != nil {
|
|
audioLogger.Error().Err(err).Msg("failed to set audio enable")
|
|
}
|
|
}
|
|
|
|
go handleClient(listener)
|
|
}
|
|
|
|
func StopNtpAudioServer() {
|
|
_, err := CallAudioCtrlAction("set_audio_enable", map[string]interface{}{"audio_enable": false})
|
|
if err != nil {
|
|
audioLogger.Error().Err(err).Msg("failed to set audio enable")
|
|
}
|
|
|
|
if audioListener != nil {
|
|
audioListener.Close()
|
|
audioListener = nil
|
|
}
|
|
|
|
if currentPort != 0 {
|
|
if err := waitUDPPortReleased(currentPort, 10, 200*time.Millisecond); err != nil {
|
|
audioLogger.Error().Err(err).Msg("port not released")
|
|
}
|
|
currentPort = 0
|
|
}
|
|
|
|
audioLogger.Info().Msg("audio server stopped")
|
|
}
|
|
|
|
func handleAudioClient(conn net.Conn) {
|
|
defer conn.Close()
|
|
|
|
audioLogger.Info().Msg("native audio socket client connected")
|
|
inboundPacket := make([]byte, maxAudioFrameSize)
|
|
var timestamp uint32
|
|
var packet rtp.Packet
|
|
|
|
for {
|
|
n, err := conn.Read(inboundPacket)
|
|
if err != nil {
|
|
audioLogger.Warn().Err(err).Msg("error during read")
|
|
return
|
|
}
|
|
|
|
if currentSession != nil {
|
|
if err := packet.Unmarshal(inboundPacket[:n]); err != nil {
|
|
audioLogger.Warn().Err(err).Msg("error unmarshalling audio socket packet")
|
|
continue
|
|
}
|
|
|
|
timestamp += timestampStep
|
|
packet.Timestamp = timestamp
|
|
buf, err := packet.Marshal()
|
|
if err != nil {
|
|
audioLogger.Warn().Err(err).Msg("error marshalling packet")
|
|
continue
|
|
}
|
|
|
|
if _, err := currentSession.AudioTrack.Write(buf); err != nil {
|
|
audioLogger.Warn().Err(err).Msg("error writing sample")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
type AudioInputState struct {
|
|
Ready bool `json:"ready"`
|
|
Error string `json:"error,omitempty"` //no_signal, no_lock, out_of_range
|
|
}
|
|
|
|
var lastAudioState AudioInputState
|
|
|
|
func HandleAudioStateMessage(event CtrlResponse) {
|
|
audioState := AudioInputState{}
|
|
err := json.Unmarshal(event.Data, &audioState)
|
|
if err != nil {
|
|
audioLogger.Warn().Err(err).Msg("Error parsing audio state json")
|
|
return
|
|
}
|
|
lastAudioState = audioState
|
|
}
|