audio: Skip the player in the state of starting, seeking or EOF

This is basically reland of
2fee7a6fe5.

Before this change, if a player's buffer was not enough for
reading, 0 value were used and this caused noises. The reading
size should be aligned with all the players.

However, there are some cases that the player should be skippped.
For example, just after a player just starts playing or seeking,
the buffer is empty. In this case, other players should not wait
for the player since decoding might take some time. Another case
is that the player reached EOF.

This change aligns the read buffer sizes but use zero values only
when the player just starts or seeks, or reaches EOF.
This commit is contained in:
Hajime Hoshi 2018-06-09 19:00:47 +09:00
parent facf184548
commit 273093b237

View File

@ -74,6 +74,25 @@ func (p *players) Read(b []byte) (int, error) {
l := len(b)
l &= mask
for player := range p.players {
if player.shouldSkip() {
continue
}
s := player.bufferSizeInBytes()
if l > s {
l = s
l &= mask
}
}
if l == 0 {
// If l is 0, all the players might reach EOF at the next update.
// However, this Read might block forever and never causes context switch
// on single-thread environment (e.g. browser).
// Call Gosched to cause context switch on purpose.
runtime.Gosched()
}
b16s := [][]int16{}
for player := range p.players {
buf, err := player.bufferToInt16(l)
@ -82,6 +101,7 @@ func (p *players) Read(b []byte) (int, error) {
}
b16s = append(b16s, buf)
}
for i := 0; i < l/2; i++ {
x := 0
for _, b16 := range b16s {
@ -349,7 +369,7 @@ func NewPlayer(context *Context, src io.ReadCloser) (*Player, error) {
players: context.players,
src: src,
sampleRate: context.sampleRate,
buf: []byte{},
buf: nil,
volume: 1,
closeCh: make(chan struct{}),
closedCh: make(chan struct{}),
@ -526,13 +546,15 @@ func (p *Player) readLoop() {
return
}
lengthInBytes := len(buf) * 2
l := lengthInBytes
if len(p.buf) < lengthInBytes && !p.srcEOF {
if p.shouldSkipImpl() {
// Return zero values.
p.proceededCh <- proceededValues{buf, nil}
break
}
lengthInBytes := len(buf) * 2
l := lengthInBytes
if l > len(p.buf) {
l = len(p.buf)
}
@ -543,7 +565,7 @@ func (p *Player) readLoop() {
p.pos += int64(l)
p.buf = p.buf[l:]
p.proceededCh <- proceededValues{buf, nil}
p.proceededCh <- proceededValues{buf[:l/2], nil}
case f := <-p.syncCh:
f()
@ -566,14 +588,46 @@ func (p *Player) sync(f func()) bool {
}
}
func (p *Player) shouldSkip() bool {
r := false
p.sync(func() {
r = p.shouldSkipImpl()
})
return r
}
func (p *Player) shouldSkipImpl() bool {
// When p.buf is nil, the player just starts playing or seeking.
// Note that this is different from len(p.buf) == 0 && p.buf != nil.
if p.buf == nil {
return true
}
if p.eofImpl() {
return true
}
return false
}
func (p *Player) bufferSizeInBytes() int {
s := 0
p.sync(func() {
s = len(p.buf)
})
return s
}
func (p *Player) eof() bool {
r := false
p.sync(func() {
r = p.srcEOF && len(p.buf) == 0
r = p.eofImpl()
})
return r
}
func (p *Player) eofImpl() bool {
return p.srcEOF && len(p.buf) == 0
}
// IsPlaying returns boolean indicating whether the player is playing.
func (p *Player) IsPlaying() bool {
return p.players.hasPlayer(p)