audio: bug fix: suspend and resume player's stopwatches

Updates #2901
This commit is contained in:
Hajime Hoshi 2024-02-01 20:28:52 +09:00
parent 6ced6987cd
commit d10636d235
2 changed files with 70 additions and 0 deletions

View File

@ -111,6 +111,9 @@ func NewContext(sampleRate int) *Context {
if err := c.playerFactory.suspend(); err != nil {
return err
}
if err := c.onSuspend(); err != nil {
return err
}
return nil
})
h.OnResumeAudio(func() error {
@ -118,6 +121,9 @@ func NewContext(sampleRate int) *Context {
if err := c.playerFactory.resume(); err != nil {
return err
}
if err := c.onResume(); err != nil {
return err
}
return nil
})
@ -197,6 +203,48 @@ func (c *Context) removePlayingPlayer(p *playerImpl) {
c.m.Unlock()
}
func (c *Context) onSuspend() error {
// A Context must not call playerImpl's functions with a lock, or this causes a deadlock (#2737).
// Copy the playerImpls and iterate them without a lock.
var players []*playerImpl
c.m.Lock()
players = make([]*playerImpl, 0, len(c.playingPlayers))
for p := range c.playingPlayers {
players = append(players, p)
}
c.m.Unlock()
for _, p := range players {
if err := p.Err(); err != nil {
return err
}
p.onContextSuspended()
}
return nil
}
func (c *Context) onResume() error {
// A Context must not call playerImpl's functions with a lock, or this causes a deadlock (#2737).
// Copy the playerImpls and iterate them without a lock.
var players []*playerImpl
c.m.Lock()
players = make([]*playerImpl, 0, len(c.playingPlayers))
for p := range c.playingPlayers {
players = append(players, p)
}
c.m.Unlock()
for _, p := range players {
if err := p.Err(); err != nil {
return err
}
p.onContextResumed()
}
return nil
}
func (c *Context) updatePlayers() error {
// A Context must not call playerImpl's functions with a lock, or this causes a deadlock (#2737).
// Copy the playerImpls and iterate them without a lock.

View File

@ -326,6 +326,28 @@ func (p *playerImpl) source() io.Reader {
return p.src
}
func (p *playerImpl) onContextSuspended() {
p.m.Lock()
defer p.m.Unlock()
if p.player == nil {
return
}
p.stopwatch.stop()
}
func (p *playerImpl) onContextResumed() {
p.m.Lock()
defer p.m.Unlock()
if p.player == nil {
return
}
if p.isPlaying() {
p.stopwatch.start()
}
}
func (p *playerImpl) updatePosition() {
p.m.Lock()
defer p.m.Unlock()