fix: Better management of stop and play behavior

Removed the need of cancelreader that didn't work on Windows
as expected, so we did away with the silly pipe to return a
ReadCloser of the output, and used the object directly from
the RadioPlayer object itself. Now when we stop, we close the
stream and the ffmpeg instance.
This commit is contained in:
José Carlos Cuevas 2023-12-17 16:05:49 +01:00
parent f5e585bfe6
commit cd3a909e61
4 changed files with 37 additions and 59 deletions

3
go.mod
View file

@ -4,8 +4,7 @@ go 1.16
require (
fyne.io/fyne/v2 v2.4.2
github.com/ebitengine/oto/v3 v3.1.0 // indirect
github.com/ebitengine/oto/v3 v3.1.0
github.com/ebitengine/purego v0.5.1 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
golang.org/x/sys v0.15.0 // indirect
)

4
go.sum
View file

@ -243,8 +243,6 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
@ -504,7 +502,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -514,7 +511,6 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

71
main.go
View file

@ -49,8 +49,6 @@ import (
"strings"
"time"
"github.com/muesli/cancelreader"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
@ -70,10 +68,6 @@ const (
Stopped
)
// Cancel Reader
var reader cancelreader.CancelReader
// helper
func check(err error) {
if err != nil {
@ -159,10 +153,10 @@ func main() {
log.Println("Starting the app")
// Create the status channel, to read from StreamPlayer and the pipe to send commands to it
pipe_chan := make(chan io.ReadCloser)
// pipe_chan := make(chan io.ReadCloser)
// Create our StreamPlayer instance
streamPlayer := StreamPlayer{player_name: PLAYER_CMD, pipe_chan: pipe_chan}
streamPlayer := StreamPlayer{player_name: PLAYER_CMD}
// Create our app and window
app := app.New()
@ -217,7 +211,7 @@ func main() {
// Here we control each time the button is pressed and update its
// appearance anytime it is clicked. We make the player start playing
// or pause.
if !streamPlayer.IsPlaying() && !streamPlayer.paused {
if !streamPlayer.IsPlaying() {
playButton.SetIcon(theme.MediaStopIcon())
playButton.SetText("(Buffering)")
streamPlayer.Load(RADIOSPIRAL_STREAM)
@ -227,9 +221,8 @@ func main() {
if playStatus == Playing {
playStatus = Stopped
playButton.SetIcon(theme.MediaPlayIcon())
streamPlayer.Pause()
streamPlayer.Stop()
} else {
reader.Cancel()
playStatus = Loading
playButton.SetText("(Buffering)")
playButton.SetIcon(theme.MediaStopIcon())
@ -242,37 +235,37 @@ func main() {
// Process the output of ffmpeg here in a separate goroutine
go func() {
for {
out_pipe := <-pipe_chan
var err error
reader, err = cancelreader.NewReader(out_pipe)
if err != nil {
log.Println("Error opening reader")
}
for {
var data [255]byte
_, err := reader.Read(data[:])
if err != nil {
log.Println(err)
break
}
lines := strings.Split(string(data[:]), "\n")
for _, line := range lines {
// Log, if enabled, the output of StreamPlayer
if *loggingToFilePtr {
log.Print("[" + streamPlayer.player_name + "] " + line)
if streamPlayer.out != nil {
for {
var data [255]byte
_, err := streamPlayer.out.Read(data[:])
if err != nil {
log.Println(err)
break
}
if strings.Contains(line, "Output #0") {
playStatus = Playing
playButton.SetText("")
}
// Check if there's an updated title and reflect it on the
// GUI
if strings.Contains(line, "StreamTitle: ") {
log.Println("Found new stream title, updating GUI")
newTitleParts := strings.Split(line, "StreamTitle: ")
nowPlayingLabel.SetText(newTitleParts[1])
lines := strings.Split(string(data[:]), "\n")
for _, line := range lines {
// Log, if enabled, the output of StreamPlayer
if *loggingToFilePtr {
log.Print("[" + streamPlayer.player_name + "] " + line)
}
if strings.Contains(line, "Output #0") {
playStatus = Playing
playButton.SetText("")
}
// Check if there's an updated title and reflect it on the
// GUI
if strings.Contains(line, "StreamTitle: ") {
log.Println("Found new stream title, updating GUI")
newTitleParts := strings.Split(line, "StreamTitle: ")
nowPlayingLabel.SetText(newTitleParts[1])
}
}
}
} else {
// To avoid high CPU usage, we wait some milliseconds before testing
// again for the change in streamPlayer.out from nil to ReadCloser
time.Sleep(200 * time.Millisecond)
}
}
}()

View file

@ -38,7 +38,7 @@ type RadioPlayer interface {
IsPlaying() bool
Play()
Mute()
Pause()
Stop()
IncVolume()
DecVolume()
Close()
@ -52,11 +52,9 @@ type StreamPlayer struct {
in io.WriteCloser
out io.ReadCloser
audio io.ReadCloser
pipe_chan chan io.ReadCloser
otoContext *oto.Context
otoPlayer *oto.Player
currentVolume float64
paused bool
}
func (player *StreamPlayer) IsPlaying() bool {
@ -114,12 +112,6 @@ func (player *StreamPlayer) Load(stream_url string) {
player.otoPlayer = player.otoContext.NewPlayer(player.audio)
// Save current volume for the mute function
player.currentVolume = player.otoPlayer.Volume()
player.paused = false
go func() {
player.pipe_chan <- player.out
}()
}
}
@ -146,6 +138,7 @@ func (player *StreamPlayer) Close() {
player.in.Close()
player.out.Close()
player.audio.Close()
player.out = nil
player.stream_url = ""
}
@ -162,12 +155,9 @@ func (player *StreamPlayer) Mute() {
}
}
func (player *StreamPlayer) Pause() {
func (player *StreamPlayer) Stop() {
if player.IsPlaying() {
if !player.paused {
player.paused = true
player.otoPlayer.Pause()
}
player.Close()
}
}