-
Notifications
You must be signed in to change notification settings - Fork 47
Expand file tree
/
Copy pathaudiosocket.go
More file actions
299 lines (241 loc) · 7.47 KB
/
audiosocket.go
File metadata and controls
299 lines (241 loc) · 7.47 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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
package audiosocket
import (
"encoding/binary"
"fmt"
"io"
"github.com/google/uuid"
)
// Message describes an audiosocket message/packet
type Message []byte
// Kind is a message type indicator
type Kind byte
const (
// KindHangup indicates the message is a hangup signal
KindHangup = 0x00
// KindID indicates the message contains the unique identifier of the call
KindID = 0x01
// KindSilence indicates the presence of silence on the line
KindSilence = 0x02
// KindDTMF indicates the message contains DTMF data
KindDTMF = 0x03
// KindSlin indicates that the message contains 16-bit, 8 kbit/s signed-linear audio data
KindSlin = 0x10
// KindSlin indicates that the message contains 16-bit, 12 kbit/s signed-linear audio data
KindSlin12 = 0x11
// KindSlin indicates that the message contains 16-bit, 16 kbit/s signed-linear audio data
KindSlin16 = 0x12
// KindSlin indicates that the message contains 16-bit, 24 kbit/s signed-linear audio data
KindSlin24 = 0x13
// KindSlin indicates that the message contains 16-bit, 32 kbit/s signed-linear audio data
KindSlin32 = 0x14
// KindSlin indicates that the message contains 16-bit, 44.1 kbit/s signed-linear audio data
KindSlin44 = 0x15
// KindSlin indicates that the message contains 16-bit, 48 kbit/s signed-linear audio data
KindSlin48 = 0x16
// KindSlin indicates that the message contains 16-bit, 96 kbit/s signed-linear audio data
KindSlin96 = 0x17
// KindSlin indicates that the message contains 16-bit, 192 kbit/s signed-linear audio data
KindSlin192 = 0x18
// KindError indicates the message contains an error code
KindError = 0xff
)
// ErrorCode indicates an error, if present
type ErrorCode byte
const (
// ErrNone indicates that no error is present
ErrNone = 0x00
// ErrAstHangup indicates that the call has hung up
ErrAstHangup = 0x01
// ErrAstFrameForwarding indicates that Asterisk had an error trying to forward an audio frame
ErrAstFrameForwarding = 0x02
// ErrAstMemory indicates that Asterisk had a memory/allocation erorr
ErrAstMemory = 0x04
// ErrUnknown indicates that the received error from Asterisk is unknown
ErrUnknown = 0xff
)
// AudioFormat defines codec-specific parameters for audio transmission
type AudioFormat struct {
Kind Kind
ChunkSize int
}
var (
// FormatSlin represents 8kHz signed linear audio format
FormatSlin = AudioFormat{
Kind: KindSlin,
ChunkSize: 320, // 8000Hz * 20ms * 2 bytes
}
// FormatSlin12 represents 12kHz signed linear audio format
FormatSlin12 = AudioFormat{
Kind: KindSlin12,
ChunkSize: 480, // 12000Hz * 20ms * 2 bytes
}
// FormatSlin16 represents 16kHz signed linear audio format
FormatSlin16 = AudioFormat{
Kind: KindSlin16,
ChunkSize: 640, // 16000Hz * 20ms * 2 bytes
}
// FormatSlin24 represents 24kHz signed linear audio format
FormatSlin24 = AudioFormat{
Kind: KindSlin24,
ChunkSize: 960, // 24000Hz * 20ms * 2 bytes
}
// FormatSlin32 represents 32kHz signed linear audio format
FormatSlin32 = AudioFormat{
Kind: KindSlin32,
ChunkSize: 1280, // 32000Hz * 20ms * 2 bytes
}
// FormatSlin44 represents 44kHz signed linear audio format
FormatSlin44 = AudioFormat{
Kind: KindSlin44,
ChunkSize: 1764, // 44100Hz * 20ms * 2 bytes
}
// FormatSlin48 represents 48kHz signed linear audio format
FormatSlin48 = AudioFormat{
Kind: KindSlin48,
ChunkSize: 1920, // 48000Hz * 20ms * 2 bytes
}
// FormatSlin96 represents 96kHz signed linear audio format
FormatSlin96 = AudioFormat{
Kind: KindSlin96,
ChunkSize: 3840, // 96000Hz * 20ms * 2 bytes
}
// FormatSlin192 represents 192kHz signed linear audio format
FormatSlin192 = AudioFormat{
Kind: KindSlin192,
ChunkSize: 7680, // 192000Hz * 20ms * 2 bytes
}
)
// AudioFormat returns the AudioFormat for this Kind
func (k Kind) AudioFormat() (AudioFormat, error) {
switch k {
case KindSlin:
return FormatSlin, nil
case KindSlin12:
return FormatSlin12, nil
case KindSlin16:
return FormatSlin16, nil
case KindSlin24:
return FormatSlin24, nil
case KindSlin32:
return FormatSlin32, nil
case KindSlin44:
return FormatSlin44, nil
case KindSlin48:
return FormatSlin48, nil
case KindSlin96:
return FormatSlin96, nil
case KindSlin192:
return FormatSlin192, nil
default:
return AudioFormat{}, fmt.Errorf("unsupported audio format: %d", k)
}
}
// ContentLength returns the length of the payload of the message
func (m Message) ContentLength() uint16 {
if len(m) < 3 {
return 0
}
return binary.BigEndian.Uint16(m[1:3])
}
// Kind returns the type of the message
func (m Message) Kind() Kind {
if len(m) < 1 {
return KindError
}
return Kind(m[0])
}
// ErrorCode returns the coded error of the message, if present
func (m Message) ErrorCode() ErrorCode {
if m.Kind() != KindError {
return ErrNone
}
if len(m) < 4 {
return ErrUnknown
}
return ErrorCode(m[3])
}
// Payload returns the data of the payload of the message
func (m Message) Payload() []byte {
sz := m.ContentLength()
if sz == 0 {
return nil
}
return m[3:]
}
// ID returns the session's unique ID if and only if the Message is the initial
// ID message. Normally, you would call GetID on the socket instead of
// manually running this function.
func (m Message) ID() (uuid.UUID, error) {
if m.Kind() != KindID {
return uuid.Nil, fmt.Errorf("wrong message type %d", m.Kind())
}
return uuid.FromBytes(m.Payload())
}
// GetID reads the unique ID from the first Message received. This should only
// be called once per connection, and it must be called before anything else is
// read from the connection.
func GetID(r io.Reader) (uuid.UUID, error) {
m, err := NextMessage(r)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to read message: %w", err)
}
return m.ID()
}
// NextMessage reads and parses the next message from an audiosocket connection
func NextMessage(r io.Reader) (Message, error) {
hdr := make([]byte, 3)
_, err := io.ReadFull(r, hdr)
if err != nil {
return nil, fmt.Errorf("failed to read header: %w", err)
}
payloadLen := binary.BigEndian.Uint16(hdr[1:])
if payloadLen < 1 {
return hdr, nil
}
payload := make([]byte, payloadLen)
_, err = io.ReadFull(r, payload)
if err != nil {
return nil, fmt.Errorf("failed to read payload: %w", err)
}
m := append(hdr, payload...)
return m, nil
}
// MessageFromData parses an audiosocket message into a Message
func MessageFromData(in []byte) Message {
return Message(in)
}
// HangupMessage creates a new Message indicating a hangup
func HangupMessage() Message {
return []byte{KindHangup, 0x00, 0x00}
}
// IDMessage creates a new Message
func IDMessage(id uuid.UUID) Message {
out := make([]byte, 3, 3+16)
out[0] = KindID
binary.BigEndian.PutUint16(out[1:], 16)
return append(out, id[:]...)
}
// SlinMessage creates a new Message from signed linear audio data
// If the input is larger than 65535 bytes, this function will panic.
func SlinMessage(in []byte) Message {
if len(in) > 65535 {
panic("audiosocket: message too large")
}
out := make([]byte, 3, 3+len(in))
out[0] = KindSlin
binary.BigEndian.PutUint16(out[1:], uint16(len(in)))
out = append(out, in...)
return out
}
// AudioMessage creates a new Message from audio data with the specified kind
// If the input is larger than 65535 bytes, this function will panic.
func AudioMessage(in []byte, kind Kind) Message {
if len(in) > 65535 {
panic("audiosocket: message too large")
}
out := make([]byte, 3, 3+len(in))
out[0] = byte(kind)
binary.BigEndian.PutUint16(out[1:], uint16(len(in)))
out = append(out, in...)
return out
}