forked from jetkvm/kvm
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathaudio.go
More file actions
173 lines (147 loc) · 4.17 KB
/
audio.go
File metadata and controls
173 lines (147 loc) · 4.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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
}