Fix #483: workaround MPV failing to begin playback of track with new sample rate on Windows
This commit is contained in:
@@ -26,6 +26,8 @@ const (
|
|||||||
// arg3: bool (shuffle)
|
// arg3: bool (shuffle)
|
||||||
cmdLoadItems
|
cmdLoadItems
|
||||||
cmdLoadRadioStation // arg: *mediaprovider.RadioStation, arg2: InsertQueueMode
|
cmdLoadRadioStation // arg: *mediaprovider.RadioStation, arg2: InsertQueueMode
|
||||||
|
|
||||||
|
cmdForceRestartPlayback
|
||||||
)
|
)
|
||||||
|
|
||||||
type playbackCommand struct {
|
type playbackCommand struct {
|
||||||
@@ -144,6 +146,13 @@ func (c *playbackCommandQueue) LoadItems(items []mediaprovider.MediaItem, insert
|
|||||||
c.cmdAvailable.Signal()
|
c.cmdAvailable.Signal()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *playbackCommandQueue) addCommand(command playbackCommand) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
c.queue = append(c.queue, command)
|
||||||
|
c.mutex.Unlock()
|
||||||
|
c.cmdAvailable.Signal()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *playbackCommandQueue) filterCommandsAndAdd(excludeTypes []playbackCommandType, command playbackCommand) {
|
func (c *playbackCommandQueue) filterCommandsAndAdd(excludeTypes []playbackCommandType, command playbackCommand) {
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
j := 0
|
j := 0
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
"github.com/dweymouth/supersonic/backend/player"
|
"github.com/dweymouth/supersonic/backend/player"
|
||||||
|
"github.com/dweymouth/supersonic/backend/player/mpv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A high-level MediaProvider-aware playback engine, serves as an
|
// A high-level MediaProvider-aware playback engine, serves as an
|
||||||
@@ -14,7 +16,9 @@ import (
|
|||||||
type PlaybackManager struct {
|
type PlaybackManager struct {
|
||||||
engine *playbackEngine
|
engine *playbackEngine
|
||||||
cmdQueue *playbackCommandQueue
|
cmdQueue *playbackCommandQueue
|
||||||
cfg *AppConfig
|
cfg *AppConfig
|
||||||
|
|
||||||
|
lastPlayTime float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlaybackManager(
|
func NewPlaybackManager(
|
||||||
@@ -23,19 +27,42 @@ func NewPlaybackManager(
|
|||||||
p player.BasePlayer,
|
p player.BasePlayer,
|
||||||
scrobbleCfg *ScrobbleConfig,
|
scrobbleCfg *ScrobbleConfig,
|
||||||
transcodeCfg *TranscodingConfig,
|
transcodeCfg *TranscodingConfig,
|
||||||
appCfg *AppConfig,
|
appCfg *AppConfig,
|
||||||
) *PlaybackManager {
|
) *PlaybackManager {
|
||||||
e := NewPlaybackEngine(ctx, s, p, scrobbleCfg, transcodeCfg)
|
e := NewPlaybackEngine(ctx, s, p, scrobbleCfg, transcodeCfg)
|
||||||
q := NewCommandQueue()
|
q := NewCommandQueue()
|
||||||
pm := &PlaybackManager{
|
pm := &PlaybackManager{
|
||||||
engine: e,
|
engine: e,
|
||||||
cmdQueue: q,
|
cmdQueue: q,
|
||||||
cfg: appCfg,
|
cfg: appCfg,
|
||||||
}
|
}
|
||||||
|
pm.workaroundWindowsPlaybackIssue()
|
||||||
go pm.runCmdQueue(ctx)
|
go pm.runCmdQueue(ctx)
|
||||||
return pm
|
return pm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PlaybackManager) workaroundWindowsPlaybackIssue() {
|
||||||
|
// See https://github.com/dweymouth/supersonic/issues/483
|
||||||
|
// On Windows, MPV sometimes fails to start playback when switching to a track
|
||||||
|
// with a different sample rate than the previous. If this is detected,
|
||||||
|
// send a command to the MPV player to force restart playback.
|
||||||
|
p.OnPlayTimeUpdate(func(curTime, _ float64, _ bool) {
|
||||||
|
p.lastPlayTime = curTime
|
||||||
|
})
|
||||||
|
p.OnSongChange(func(mediaprovider.MediaItem, *mediaprovider.Track) {
|
||||||
|
if p.NowPlayingIndex() != len(p.engine.playQueue) && p.PlayerStatus().State == player.Playing {
|
||||||
|
p.lastPlayTime = 0
|
||||||
|
go func() {
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
if p.lastPlayTime == 0 {
|
||||||
|
log.Println("Play stall detected!")
|
||||||
|
p.cmdQueue.addCommand(playbackCommand{Type: cmdForceRestartPlayback})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) CurrentPlayer() player.BasePlayer {
|
func (p *PlaybackManager) CurrentPlayer() player.BasePlayer {
|
||||||
return p.engine.CurrentPlayer()
|
return p.engine.CurrentPlayer()
|
||||||
}
|
}
|
||||||
@@ -430,6 +457,11 @@ func (p *PlaybackManager) runCmdQueue(ctx context.Context) {
|
|||||||
c.Arg.(*mediaprovider.RadioStation),
|
c.Arg.(*mediaprovider.RadioStation),
|
||||||
c.Arg2.(InsertQueueMode),
|
c.Arg2.(InsertQueueMode),
|
||||||
)
|
)
|
||||||
|
case cmdForceRestartPlayback:
|
||||||
|
if mpv, ok := p.engine.CurrentPlayer().(*mpv.Player); ok {
|
||||||
|
log.Println("Force-restarting MPV playback")
|
||||||
|
mpv.ForceRestartPlayback()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,6 +311,11 @@ func (p *Player) Continue() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Player) ForceRestartPlayback() error {
|
||||||
|
p.mpv.SetProperty("pause", mpv.FORMAT_FLAG, true)
|
||||||
|
return p.mpv.SetProperty("pause", mpv.FORMAT_FLAG, false)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the current status of the player.
|
// Get the current status of the player.
|
||||||
func (p *Player) GetStatus() player.Status {
|
func (p *Player) GetStatus() player.Status {
|
||||||
if !p.initialized {
|
if !p.initialized {
|
||||||
|
|||||||
Reference in New Issue
Block a user