-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathreader.go
More file actions
95 lines (73 loc) · 1.83 KB
/
reader.go
File metadata and controls
95 lines (73 loc) · 1.83 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
package icyproxy
import (
"bytes"
"io"
"sync"
)
const IcyReaderInterval = 16000 // bytes
type IcyReader struct {
mu sync.Mutex
AudioData io.Reader
title string
audioBytesSent int
pendingMetadata []byte
}
var _ io.Reader = &IcyReader{}
func makeIcyBlock(streamTitle string) []byte {
// metadata block: 1 byte for size, then StreamTitle='title';
// len("StreamTitle='';") == 15
const BaseSize = 15
const MaxTitleSize = 255*16 - BaseSize
if len(streamTitle) > MaxTitleSize {
streamTitle = streamTitle[:MaxTitleSize]
}
blockSize := 16 * (1 + (len(streamTitle)+BaseSize)/16)
var res bytes.Buffer
res.Grow(1 + blockSize)
res.WriteByte(byte(blockSize / 16))
res.WriteString("StreamTitle='")
res.WriteString(streamTitle)
res.WriteString("';")
pad := 1 + blockSize - res.Len()
for i := 0; i < pad; i++ {
res.WriteByte(0)
}
return res.Bytes()
}
func (r *IcyReader) Read(buf []byte) (int, error) {
r.mu.Lock()
defer r.mu.Unlock()
metadataBytesSent := 0
if r.pendingMetadata != nil {
end := len(r.pendingMetadata)
if end > len(buf) {
end = len(buf)
}
copy(buf, r.pendingMetadata[:end])
buf = buf[end:]
if end == len(r.pendingMetadata) {
// could send all the metadata
metadataBytesSent = len(r.pendingMetadata)
r.pendingMetadata = nil
r.audioBytesSent = 0
} else {
// will send the rest of the metadata later
return len(buf), nil
}
}
bytesUntilNextMetadata := IcyReaderInterval - r.audioBytesSent
if len(buf) >= bytesUntilNextMetadata {
buf = buf[:bytesUntilNextMetadata]
}
n, err := r.AudioData.Read(buf)
r.audioBytesSent += n
if r.audioBytesSent == IcyReaderInterval {
r.pendingMetadata = makeIcyBlock(r.title)
}
return n + metadataBytesSent, err
}
func (r *IcyReader) SetTitle(title string) {
r.mu.Lock()
defer r.mu.Unlock()
r.title = title
}