-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmpg123.go
100 lines (85 loc) · 2.02 KB
/
mpg123.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
package mpg123
import (
"io"
"net/http"
"os/exec"
)
// Player is an abstraction for audio player using existing operating
// system mpg123 command.
type Player struct {
State int32
currentURL string
mpg123 *exec.Cmd
mpg123Handler io.WriteCloser
httpBodyHandler io.ReadCloser
}
const (
stateStopped = iota
statePlaying
)
// NewPlayer creates and initialize a new MPG123Player
func NewPlayer() (*Player, error) {
err := checkMPG123Path()
p := new(Player)
return p, err
}
// Play uses mpg123 command to play audio with stream URL.
// TODO: Add ability to play a file
func (p *Player) Play(StreamURL string) (err error) {
if p.State == statePlaying {
p.Stop()
return p.Play(StreamURL)
}
// mpg123 setup
// TODO Reduce mpg123 buffer to be able to stream faster ?
mpg123 := exec.Command("mpg123", "-q", "-")
stdin, err := mpg123.StdinPipe()
if err != nil {
stdin.Close()
return
}
err = mpg123.Start()
if err != nil {
stdin.Close()
return
}
// Setup HTTP stream
response, err := http.Get(StreamURL)
if err != nil {
return
}
// Setup mpg123 pipe
p.currentURL = StreamURL
p.mpg123 = mpg123
p.mpg123Handler = stdin
p.httpBodyHandler = response.Body
p.State = statePlaying
// Start streaming
go streamData(stdin, response.Body)
return
}
func streamData(stdin io.WriteCloser, body io.ReadCloser) {
defer stdin.Close()
defer body.Close()
io.Copy(stdin, body)
// We ignore errors for now
// Errors typically happen when the stream source was disconnected or
// force closed ou our side to interrupt player
}
// Stop terminates HTPP request and MPG123 command stream.
func (p *Player) Stop() {
if p.State == stateStopped {
return
}
p.State = stateStopped
p.httpBodyHandler.Close()
p.httpBodyHandler = nil
p.mpg123Handler.Close()
p.mpg123.Process.Kill() // We force kill to stop sound as fast as possible
p.mpg123.Wait() // Wait ensures Zombie process are cleaned up
p.mpg123Handler = nil
}
func checkMPG123Path() error {
_, err := exec.LookPath("mpg123")
return err
}