audio/internal/readerdriver: Bug fix: Avoid AudioQueueReset

Instead, call AudioQueueStop and discard the current AudioQueue.

Closes #1680
Updates #1650
This commit is contained in:
Hajime Hoshi 2021-06-26 01:11:33 +09:00
parent 4ce90a564b
commit 371bbfc0f2

View File

@ -33,7 +33,7 @@ import (
) )
func IsAvailable() bool { func IsAvailable() bool {
return false return true
} }
type audioQueuePoolItem struct { type audioQueuePoolItem struct {
@ -495,20 +495,11 @@ func (p *playerImpl) resetImpl() {
return return
} }
if len(p.unqueuedBufs) < 2 { // AudioQueueReset is not efficient (#1650, #1680).
if osstatus := C.AudioQueuePause(p.audioQueue); osstatus != C.noErr && p.err == nil { // Discard the current AudioQueue and recreate one when playing this player again.
p.setErrorImpl(fmt.Errorf("readerdriver: AudioQueuePause failed: %d", osstatus)) if err := p.closeAudioQueue(); err != nil {
return p.setErrorImpl(err)
} return
// AudioQueueReset invokes the callback directry.
q := p.audioQueue
p.m.Unlock()
osstatus := C.AudioQueueReset(q)
p.m.Lock()
if osstatus != C.noErr && p.err == nil {
p.setErrorImpl(fmt.Errorf("readerdriver: AudioQueueReset failed: %d", osstatus))
return
}
} }
p.state = playerPaused p.state = playerPaused
@ -575,35 +566,45 @@ func (p *playerImpl) Close() error {
} }
func (p *playerImpl) closeImpl() error { func (p *playerImpl) closeImpl() error {
if p.audioQueue != nil { if err := p.closeAudioQueue(); err != nil && p.err == nil {
// Even if reuseLater is true, AudioQueuePause is not efficient for reusing. // setErrorImpl calls closeImpl. Do not call this.
// AudioQueueStart takes long if the AudioQueueStop is not called. p.err = err
// AudioQueueStop might invoke AudioQueueReset. Unlock the mutex here to avoid a deadlock.
q := p.audioQueue
p.m.Unlock()
osstatus := C.AudioQueueStop(q, C.true)
p.m.Lock()
if osstatus != C.noErr && p.err == nil {
// setErrorImpl calls closeImpl. Do not call this.
p.err = fmt.Errorf("readerdriver: AudioQueueStop failed: %d", osstatus)
}
// All the AudioQueueBuffers are already dequeued. It is safe to dispose the AudioQueue and its buffers.
if err := p.context.audioQueuePool.Put(p.audioQueue); err != nil && p.err == nil {
p.err = err
}
p.m.Unlock()
thePlayers.remove(p.audioQueue)
p.m.Lock()
p.audioQueue = nil
} }
p.state = playerClosed p.state = playerClosed
return p.err return p.err
} }
func (p *playerImpl) closeAudioQueue() error {
if p.audioQueue == nil {
return nil
}
// Even if reuseLater is true, AudioQueuePause is not efficient for reusing.
// AudioQueueStart takes long if the AudioQueueStop is not called.
// AudioQueueStop might invoke AudioQueueReset. Unlock the mutex here to avoid a deadlock.
q := p.audioQueue
p.m.Unlock()
osstatus := C.AudioQueueStop(q, C.true)
p.m.Lock()
if osstatus != C.noErr && p.err == nil {
return fmt.Errorf("readerdriver: AudioQueueStop failed: %d", osstatus)
}
// All the AudioQueueBuffers are already dequeued. It is safe to dispose the AudioQueue and its buffers.
if err := p.context.audioQueuePool.Put(p.audioQueue); err != nil && p.err == nil {
return err
}
p.m.Unlock()
thePlayers.remove(p.audioQueue)
p.m.Lock()
p.audioQueue = nil
return nil
}
//export ebiten_readerdriver_render //export ebiten_readerdriver_render
func ebiten_readerdriver_render(inUserData unsafe.Pointer, inAQ C.AudioQueueRef, inBuffer C.AudioQueueBufferRef) { func ebiten_readerdriver_render(inUserData unsafe.Pointer, inAQ C.AudioQueueRef, inBuffer C.AudioQueueBufferRef) {
p := thePlayers.get(inAQ) p := thePlayers.get(inAQ)