-
Notifications
You must be signed in to change notification settings - Fork 0
/
streamerOMXPlayer.go
200 lines (166 loc) · 3.95 KB
/
streamerOMXPlayer.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
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
package piplayer
import (
"errors"
"io"
"log"
"os"
"os/exec"
)
// OMXPlayer uses OMXPlayer to stream H.264 files to the screen.
// It implements the Streamer interface.
type OMXPlayer struct {
Supports []string
status int
closing chan error
cmd *exec.Cmd
cmdStdinPipe io.WriteCloser
playbackRate int
audioOutput string
}
// Open starts a video file in the OMXPlayer streamer.
func (o *OMXPlayer) Open(filename string, status chan string, test string, debug bool) error {
o.status = statusStarting
// Attempt to close OMXPlayer in case it's already running.
if err := o.Close(); err != nil {
return err
}
// Default audioOutput to "both".
if o.audioOutput == "" {
log.Printf("WARNING: 'audioOutput not configured. Defaulting to \"both\"'\n")
o.audioOutput = "both"
}
flags := []string{
"-b",
"-o", o.audioOutput,
filename,
}
// TODO: Check for loop
// if loop {
// flags = append(flags, "--loop")
// }
var err error
o.cmd = exec.Command("omxplayer", flags...)
o.cmdStdinPipe, err = o.cmd.StdinPipe()
if err != nil {
return err
}
if debug {
o.cmd.Stdout = os.Stdout
}
o.cmd.Stderr = os.Stderr
err = o.cmd.Start()
if err != nil {
o.status = statusError
return err
}
o.status = statusPlaying
o.playbackRate = 1
// Listen for when OMX player ends in a new goroutine
go o.wait(status)
return nil
}
// wait waits for OMXPlayer to end so it can clean things up.
func (o *OMXPlayer) wait(status chan string) error {
// Block till the command/process is finished.
err := o.cmd.Wait()
prevStatus := o.status
o.status = statusStopped
if prevStatus == statusClosing {
o.closing <- err
} else {
// Start the next item by sending a next message
// on the status channel, back to the Player interface.
status <- "next"
}
return err
}
// Close stops the video and closes the streamer process.
func (o *OMXPlayer) Close() error {
if o.status != statusPlaying {
return nil
}
o.status = statusClosing
o.closing = make(chan error)
defer close(o.closing)
o.pipe("q")
// Block till OMXPlayer exits
err := <-o.closing
// Ignore exit status 3
if err != nil && err.Error() != "exit status 3" {
return err
}
return nil
}
// pipe Pipes a message to the command
func (o *OMXPlayer) pipe(message string) error {
_, err := o.cmdStdinPipe.Write([]byte(message))
return err
}
// Play resumes video playback.
func (o *OMXPlayer) Play() error {
// Ignore if the player isn't in a paused state
if o.status != statusPaused {
return nil
}
return o.pipe("p")
}
// Pause pauses the video playback.
func (o *OMXPlayer) Pause() error {
// Ignore if the player isn't in a playing state.
if o.status != statusPlaying {
return nil
}
return o.pipe("p")
}
// PlaybackRate sets the video playback speed.
func (o *OMXPlayer) PlaybackRate(speed int) error {
if o.status != statusPlaying && o.status != statusPaused {
return errors.New("can't set the PlaybackRate. Video not running")
}
for speed < o.playbackRate {
o.pipe("2")
o.playbackRate--
}
for speed > o.playbackRate {
o.pipe("1")
o.playbackRate++
}
return nil
}
// Seek seeks the video to a certain time.
func (o *OMXPlayer) Seek(direction int) error {
if o.status != statusPlaying && o.status != statusPaused {
return errors.New("can't seek the video. Video not running")
}
switch direction {
case -2:
o.pipe("\x1b[B")
case -1:
o.pipe("\x1b[D")
case 1:
o.pipe("\x1b[C")
case 2:
o.pipe("\x1b[A")
}
return nil
}
// Chapter brings the video to the specified chatper.
func (o *OMXPlayer) Chapter(index int) error {
// TODO: everything
return nil
}
// Volume sets the video volume.
func (o *OMXPlayer) Volume(vol int) error {
// TODO: everything
return nil
}
// AudioStream sets the specified video audio stream.
func (o *OMXPlayer) AudioStream(index int) error {
// TODO: everything
return nil
}
// SubtitleStream sets the subtitle stream.
func (o *OMXPlayer) SubtitleStream(index int) error {
// TODO: everything
return nil
}