/
audio.go
105 lines (86 loc) · 2.17 KB
/
audio.go
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
package raopd
import (
"context"
"crypto/aes"
"crypto/cipher"
"errors"
"io"
"os"
"sync"
"time"
"github.com/maghul/go.alac"
)
var alacNotInitialized = errors.New("Alac has not been initialized")
type audioStream struct {
audioWriter io.Writer
ctx context.Context
count int
}
type audioStreams struct {
audioBuffer []byte
mode cipher.BlockMode
aeskey cipher.Block
aesiv []byte
alac *alac.Alac
streamsMutex sync.Mutex
streams []*audioStream
}
var audiolog = getLogger("raopd.audio", "Audio Output")
func (r *audioStreams) initAlac(rtpmap, fmtpstr string) error {
var err error
r.alac, err = alac.NewFromFmtp(fmtpstr)
return err
}
func (r *audioStreams) newStream(ctx context.Context, w io.Writer) {
audiolog.Debug.Println("audioStreams:newStream w=", w)
// Sets a timeout count of 10.
ns := &audioStream{w, ctx, 10}
r.streamsMutex.Lock()
defer r.streamsMutex.Unlock()
r.streams = append(r.streams, ns)
}
func (r *audioStreams) handleAudioPacket(pkt *rtpPacket) {
r.mode = cipher.NewCBCDecrypter(r.aeskey, r.aesiv)
ciphertext := pkt.content[12:]
l := len(ciphertext) / 16
l *= 16
ciphertext = ciphertext[:l]
if len(ciphertext)%aes.BlockSize != 0 {
audiolog.Info.Println("ciphertext is not a multiple of the block size")
os.Exit(0)
}
r.mode.CryptBlocks(ciphertext, ciphertext)
r.audioBuffer = r.alac.Decode(pkt.content[12:])
pkt.Reclaim()
r.writeToStreams(r.audioBuffer)
}
const audioTimeout = time.Millisecond
func (r *audioStreams) writeToStreams(b []byte) {
r.streamsMutex.Lock()
defer r.streamsMutex.Unlock()
jj := 0
for ii, as := range r.streams {
ctx := as.ctx
select {
case <-ctx.Done():
audiolog.Debug.Println("Context closed audio output ", as)
default:
of := as.audioWriter
_, err := of.Write(b)
if err != nil {
audiolog.Debug.Println("Closing audio output ", as, ", on error=", err)
} else {
r.streams[jj] = r.streams[ii]
jj++
}
}
}
r.streams = r.streams[0:jj]
}
func (a *audioStreams) rtptoms(rtp int64) (int, error) {
if a.alac == nil {
return 0, alacNotInitialized
}
sampleRate := a.alac.SampleRate()
return int((rtp * 1000) / int64(sampleRate)), nil
}