keep hanging DLNA player from hanging Supersonic when quitting/switching players
This commit is contained in:
@@ -61,6 +61,12 @@ func (c *playbackCommandQueue) C() <-chan playbackCommand {
|
||||
return c.nextChan
|
||||
}
|
||||
|
||||
func (c *playbackCommandQueue) Clear() {
|
||||
c.mutex.Lock()
|
||||
c.queue = nil
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (c *playbackCommandQueue) Stop() {
|
||||
c.filterCommandsAndAdd([]playbackCommandType{cmdContinue, cmdPause, cmdStop},
|
||||
playbackCommand{Type: cmdStop})
|
||||
|
||||
@@ -3,7 +3,6 @@ package backend
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"time"
|
||||
@@ -157,15 +156,15 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) error {
|
||||
case player.Stopped:
|
||||
// nothing
|
||||
case player.Playing:
|
||||
if err := p.Pause(); err != nil {
|
||||
return fmt.Errorf("failed to pause: %v", err)
|
||||
}
|
||||
needToUnpause = true
|
||||
fallthrough
|
||||
case player.Paused:
|
||||
p.pendingPlayerChangeTimePos = stat.TimePos
|
||||
p.pendingPlayerChange = true
|
||||
}
|
||||
if err := p.player.Stop(true); err != nil {
|
||||
log.Printf("failed to stop player: %v", err)
|
||||
}
|
||||
|
||||
oldVol := p.player.GetVolume()
|
||||
if _, isMPV := p.player.(*mpv.Player); !isMPV {
|
||||
@@ -293,7 +292,7 @@ func (p *playbackEngine) IsSeeking() bool {
|
||||
}
|
||||
|
||||
func (p *playbackEngine) Stop() error {
|
||||
return p.player.Stop()
|
||||
return p.player.Stop(false)
|
||||
}
|
||||
|
||||
func (p *playbackEngine) Pause() error {
|
||||
@@ -332,7 +331,7 @@ func (p *playbackEngine) LoadTracks(tracks []*mediaprovider.Track, insertQueueMo
|
||||
|
||||
func (p *playbackEngine) doLoaditems(items []mediaprovider.MediaItem, insertQueueMode InsertQueueMode, shuffle bool) error {
|
||||
if insertQueueMode == Replace {
|
||||
p.player.Stop()
|
||||
p.player.Stop(false)
|
||||
p.nowPlayingIdx = -1
|
||||
p.playQueue = nil
|
||||
}
|
||||
@@ -358,7 +357,7 @@ func (p *playbackEngine) doLoaditems(items []mediaprovider.MediaItem, insertQueu
|
||||
|
||||
func (p *playbackEngine) LoadRadioStation(radio *mediaprovider.RadioStation, insertMode InsertQueueMode) {
|
||||
if insertMode == Replace {
|
||||
p.player.Stop()
|
||||
p.player.Stop(false)
|
||||
p.nowPlayingIdx = -1
|
||||
p.playQueue = nil
|
||||
}
|
||||
@@ -384,7 +383,7 @@ func (p *playbackEngine) LoadRadioStation(radio *mediaprovider.RadioStation, ins
|
||||
// Stop playback and clear the play queue.
|
||||
func (p *playbackEngine) StopAndClearPlayQueue() {
|
||||
changed := len(p.playQueue) > 0
|
||||
p.player.Stop()
|
||||
p.player.Stop(false)
|
||||
p.playQueue = nil
|
||||
p.nowPlayingIdx = -1
|
||||
if changed {
|
||||
|
||||
@@ -139,6 +139,7 @@ func (p *PlaybackManager) CurrentRemotePlayer() *RemotePlaybackDevice {
|
||||
}
|
||||
|
||||
func (p *PlaybackManager) SetRemotePlayer(rp *RemotePlaybackDevice) error {
|
||||
p.cmdQueue.Clear()
|
||||
if rp == nil {
|
||||
if err := p.engine.SetPlayer(p.localPlayer); err != nil {
|
||||
return err
|
||||
|
||||
@@ -41,7 +41,8 @@ type proxyMapEntry struct {
|
||||
type DLNAPlayer struct {
|
||||
player.BasePlayerCallbackImpl
|
||||
|
||||
destroyed bool
|
||||
destroyed bool
|
||||
cancelRequest context.CancelFunc
|
||||
|
||||
avTransport *avtransport.Client
|
||||
renderControl *renderingcontrol.Client
|
||||
@@ -122,15 +123,31 @@ func NewDLNAPlayer(device *device.MediaRenderer) (*DLNAPlayer, error) {
|
||||
}
|
||||
|
||||
func (d *DLNAPlayer) SetVolume(vol int) error {
|
||||
return d.renderControl.SetVolume(context.Background(), vol)
|
||||
if d.destroyed {
|
||||
return nil
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
d.cancelRequest = cancel
|
||||
defer cancel()
|
||||
return d.renderControl.SetVolume(ctx, vol)
|
||||
}
|
||||
|
||||
func (d *DLNAPlayer) GetVolume() int {
|
||||
vol, _ := d.renderControl.GetVolume(context.Background())
|
||||
if d.destroyed {
|
||||
return 0
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
d.cancelRequest = cancel
|
||||
defer cancel()
|
||||
vol, _ := d.renderControl.GetVolume(ctx)
|
||||
return vol
|
||||
}
|
||||
|
||||
func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadata, startTime float64) error {
|
||||
if d.destroyed {
|
||||
return nil
|
||||
}
|
||||
|
||||
d.ensureSetupProxy()
|
||||
|
||||
d.metaLock.Lock()
|
||||
@@ -179,17 +196,25 @@ func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadat
|
||||
}
|
||||
|
||||
func (d *DLNAPlayer) playAVTransportMedia(media *avtransport.MediaItem) error {
|
||||
err := d.avTransport.SetAVTransportMedia(context.Background(), media)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
d.cancelRequest = cancel
|
||||
defer cancel()
|
||||
|
||||
err := d.avTransport.SetAVTransportMedia(ctx, media)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.avTransport.Play(context.Background()); err != nil {
|
||||
if err := d.avTransport.Play(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadata) error {
|
||||
if d.destroyed {
|
||||
return nil
|
||||
}
|
||||
|
||||
var media *avtransport.MediaItem
|
||||
d.metaLock.Lock()
|
||||
d.nextTrackMeta = meta
|
||||
@@ -203,7 +228,11 @@ func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadat
|
||||
Title: meta.Name,
|
||||
}
|
||||
}
|
||||
err := d.avTransport.SetNextAVTransportMedia(context.Background(), media)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
d.cancelRequest = cancel
|
||||
defer cancel()
|
||||
err := d.avTransport.SetNextAVTransportMedia(ctx, media)
|
||||
if err != nil {
|
||||
d.metaLock.Lock()
|
||||
d.failedToSetNext = true
|
||||
@@ -214,19 +243,23 @@ func (d *DLNAPlayer) SetNextFile(url string, meta mediaprovider.MediaItemMetadat
|
||||
}
|
||||
|
||||
func (d *DLNAPlayer) Continue() error {
|
||||
if d.state == playing {
|
||||
if d.destroyed || d.state == playing {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
d.cancelRequest = cancel
|
||||
defer cancel()
|
||||
|
||||
if d.pendingSeek {
|
||||
d.pendingSeek = false
|
||||
err := d.avTransport.Seek(context.Background(), int(d.pendingSeekSecs))
|
||||
err := d.avTransport.Seek(ctx, int(d.pendingSeekSecs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := d.avTransport.Play(context.Background()); err != nil {
|
||||
if err := d.avTransport.Play(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
d.metaLock.Lock()
|
||||
@@ -240,11 +273,14 @@ func (d *DLNAPlayer) Continue() error {
|
||||
}
|
||||
|
||||
func (d *DLNAPlayer) Pause() error {
|
||||
if d.state != playing {
|
||||
if d.destroyed || d.state != playing {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := d.avTransport.Pause(context.Background()); err != nil {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
d.cancelRequest = cancel
|
||||
defer cancel()
|
||||
if err := d.avTransport.Pause(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
d.setTrackChangeTimer(0)
|
||||
@@ -254,12 +290,29 @@ func (d *DLNAPlayer) Pause() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DLNAPlayer) Stop() error {
|
||||
func (d *DLNAPlayer) Stop(force bool) error {
|
||||
if d.destroyed {
|
||||
return nil
|
||||
}
|
||||
if force && d.cancelRequest != nil {
|
||||
d.cancelRequest()
|
||||
}
|
||||
|
||||
switch d.state {
|
||||
case stopped:
|
||||
return nil
|
||||
case playing:
|
||||
if err := d.avTransport.Pause(context.Background()); err != nil {
|
||||
var ctx context.Context
|
||||
var cancel context.CancelFunc
|
||||
if force {
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
|
||||
} else {
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
}
|
||||
d.cancelRequest = cancel
|
||||
defer cancel()
|
||||
|
||||
if err := d.avTransport.Pause(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
fallthrough
|
||||
@@ -276,6 +329,10 @@ func (d *DLNAPlayer) Stop() error {
|
||||
}
|
||||
|
||||
func (d *DLNAPlayer) SeekSeconds(secs float64) error {
|
||||
if d.destroyed {
|
||||
return nil
|
||||
}
|
||||
|
||||
if d.state == paused {
|
||||
d.pendingSeek = true
|
||||
d.pendingSeekSecs = secs
|
||||
@@ -309,7 +366,9 @@ func (d *DLNAPlayer) SeekSeconds(secs float64) error {
|
||||
|
||||
func (d *DLNAPlayer) sendSeekCmd(secs float64) error {
|
||||
d.seeking = true
|
||||
if err := d.avTransport.Seek(context.Background(), int(secs)); err != nil {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
if err := d.avTransport.Seek(ctx, int(secs)); err != nil {
|
||||
d.seeking = false
|
||||
return err
|
||||
}
|
||||
@@ -346,8 +405,16 @@ func (d *DLNAPlayer) curPlayPos() time.Duration {
|
||||
|
||||
func (d *DLNAPlayer) Destroy() {
|
||||
d.destroyed = true
|
||||
d.setTrackChangeTimer(0)
|
||||
if d.cancelRequest != nil {
|
||||
d.cancelRequest()
|
||||
}
|
||||
|
||||
if d.proxyServer != nil {
|
||||
go d.proxyServer.Shutdown(context.Background())
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
go d.proxyServer.Shutdown(ctx)
|
||||
d.proxyServer = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ func (j *JukeboxPlayer) Pause() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *JukeboxPlayer) Stop() error {
|
||||
func (j *JukeboxPlayer) Stop(_ bool) error {
|
||||
if j.state == stopped {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ func (p *Player) PlayFile(url string, _ mediaprovider.MediaItemMetadata, startTi
|
||||
}
|
||||
|
||||
// Stops playback and clears the play queue.
|
||||
func (p *Player) Stop() error {
|
||||
func (p *Player) Stop(_ bool) error {
|
||||
if !p.initialized {
|
||||
return ErrUnitialized
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package player
|
||||
|
||||
import "github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||
import (
|
||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||
)
|
||||
|
||||
type URLPlayer interface {
|
||||
BasePlayer
|
||||
@@ -17,7 +19,7 @@ type TrackPlayer interface {
|
||||
type BasePlayer interface {
|
||||
Continue() error
|
||||
Pause() error
|
||||
Stop() error
|
||||
Stop(force bool) error
|
||||
|
||||
SeekSeconds(secs float64) error
|
||||
IsSeeking() bool
|
||||
|
||||
Reference in New Issue
Block a user