首次推送
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
package teamspeak
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
receiveSampleRate = 48000
|
||||
receiveChannels = 2
|
||||
receiveFrameSamplesPerChannel = 960
|
||||
receiveFrameInterleavedSamples = receiveFrameSamplesPerChannel * receiveChannels
|
||||
receiveFrameBytes = receiveFrameInterleavedSamples * 2
|
||||
receiveMaxDecodeSamplesPerChannel = 5760
|
||||
receivePrebuffer = 3
|
||||
receiveMaxPackets = 12
|
||||
receiveMaxPLC = 3
|
||||
)
|
||||
|
||||
// voiceDecoder decodes one remote Opus stream. It is intentionally private so
|
||||
// gomobile does not try to expose the decoder interface in the public AAR API.
|
||||
// Android production wiring will provide a libopus-backed implementation.
|
||||
type voiceDecoder interface {
|
||||
Decode(packet []byte) ([]int16, error)
|
||||
DecodeLost() ([]int16, error)
|
||||
Release()
|
||||
}
|
||||
|
||||
type voiceDecoderFactory interface {
|
||||
NewvoiceDecoder(clientID int, codec int) (voiceDecoder, error)
|
||||
}
|
||||
|
||||
type voicePacket struct {
|
||||
clientID int
|
||||
codec int
|
||||
sequence uint16
|
||||
data []byte
|
||||
at time.Time
|
||||
}
|
||||
|
||||
type audioCommand struct {
|
||||
kind string
|
||||
clientID int
|
||||
muted bool
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// receiveAudio owns all receive-side decoder, jitter, mute, speaking, and
|
||||
// mixing state on one goroutine. Its ingress method is safe for protocol
|
||||
// callback goroutines and never waits for decoding or playback.
|
||||
type receiveAudio struct {
|
||||
factory voiceDecoderFactory
|
||||
packets uint64
|
||||
decoded uint64
|
||||
mixed uint64
|
||||
ingress chan voicePacket
|
||||
commands chan audioCommand
|
||||
stop chan struct{}
|
||||
done chan struct{}
|
||||
onPCM func([]byte)
|
||||
onSpeaking func(int, bool)
|
||||
onDrop func()
|
||||
stopOnce sync.Once
|
||||
startOnce sync.Once
|
||||
stateMu sync.Mutex
|
||||
started bool
|
||||
}
|
||||
|
||||
func newReceiveAudio(factory voiceDecoderFactory, onPCM func([]byte), onSpeaking func(int, bool)) *receiveAudio {
|
||||
return &receiveAudio{
|
||||
factory: factory, ingress: make(chan voicePacket, 64), commands: make(chan audioCommand, 32),
|
||||
stop: make(chan struct{}), done: make(chan struct{}), onPCM: onPCM, onSpeaking: onSpeaking,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *receiveAudio) start() {
|
||||
r.startOnce.Do(func() {
|
||||
log.Printf("[TSVoice] receive worker started factory=%T", r.factory)
|
||||
r.stateMu.Lock()
|
||||
r.started = true
|
||||
r.stateMu.Unlock()
|
||||
go r.run()
|
||||
})
|
||||
}
|
||||
|
||||
func (r *receiveAudio) stopAndWait() {
|
||||
r.stateMu.Lock()
|
||||
started := r.started
|
||||
r.stateMu.Unlock()
|
||||
if !started {
|
||||
return
|
||||
}
|
||||
r.stopOnce.Do(func() { close(r.stop) })
|
||||
<-r.done
|
||||
}
|
||||
|
||||
func (r *receiveAudio) enqueue(clientID, codec int, sequence uint16, data []byte) {
|
||||
if clientID <= 0 || (codec != 4 && codec != 5) || len(data) == 0 {
|
||||
return
|
||||
}
|
||||
packet := voicePacket{clientID: clientID, codec: codec, sequence: sequence, data: append([]byte(nil), data...), at: time.Now()}
|
||||
count := atomic.AddUint64(&r.packets, 1)
|
||||
if count <= 5 || count%250 == 0 {
|
||||
log.Printf("[TSVoice] ingress count=%d client=%d codec=%d sequence=%d payload=%d", count, clientID, codec, sequence, len(data))
|
||||
}
|
||||
select {
|
||||
case r.ingress <- packet:
|
||||
default:
|
||||
if r.onDrop != nil {
|
||||
r.onDrop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *receiveAudio) command(cmd audioCommand) {
|
||||
select {
|
||||
case r.commands <- cmd:
|
||||
default:
|
||||
if cmd.done != nil {
|
||||
close(cmd.done)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *receiveAudio) run() {
|
||||
defer close(r.done)
|
||||
timelines := make(map[int]*receiveTimeline)
|
||||
ticker := time.NewTicker(20 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-r.stop:
|
||||
for id, t := range timelines {
|
||||
t.release()
|
||||
if t.speaking && r.onSpeaking != nil {
|
||||
r.onSpeaking(id, false)
|
||||
}
|
||||
}
|
||||
return
|
||||
case cmd := <-r.commands:
|
||||
r.applyCommand(timelines, cmd)
|
||||
case p := <-r.ingress:
|
||||
r.accept(timelines, p)
|
||||
case now := <-ticker.C:
|
||||
// Drain a bounded amount before every playout deadline so ingress
|
||||
// bursts cannot indefinitely postpone a 20 ms output frame.
|
||||
for i := 0; i < 32; i++ {
|
||||
select {
|
||||
case cmd := <-r.commands:
|
||||
r.applyCommand(timelines, cmd)
|
||||
case p := <-r.ingress:
|
||||
r.accept(timelines, p)
|
||||
default:
|
||||
i = 32
|
||||
}
|
||||
}
|
||||
r.tick(timelines, now)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *receiveAudio) accept(timelines map[int]*receiveTimeline, p voicePacket) {
|
||||
t := timelines[p.clientID]
|
||||
if t == nil {
|
||||
if r.factory == nil {
|
||||
return
|
||||
}
|
||||
decoder, err := r.factory.NewvoiceDecoder(p.clientID, p.codec)
|
||||
if err != nil || decoder == nil {
|
||||
log.Printf("[TSVoice] decoder create failed client=%d codec=%d err=%v", p.clientID, p.codec, err)
|
||||
return
|
||||
}
|
||||
log.Printf("[TSVoice] decoder created client=%d codec=%d", p.clientID, p.codec)
|
||||
t = newReceiveTimeline(p.clientID, decoder)
|
||||
timelines[p.clientID] = t
|
||||
}
|
||||
t.push(p)
|
||||
}
|
||||
|
||||
func (r *receiveAudio) applyCommand(timelines map[int]*receiveTimeline, cmd audioCommand) {
|
||||
switch cmd.kind {
|
||||
case "mute":
|
||||
if t := timelines[cmd.clientID]; t != nil {
|
||||
t.muted = cmd.muted
|
||||
}
|
||||
case "remove":
|
||||
if t := timelines[cmd.clientID]; t != nil {
|
||||
t.release()
|
||||
if t.speaking && r.onSpeaking != nil {
|
||||
r.onSpeaking(cmd.clientID, false)
|
||||
}
|
||||
delete(timelines, cmd.clientID)
|
||||
}
|
||||
case "clear":
|
||||
for id, t := range timelines {
|
||||
t.release()
|
||||
if t.speaking && r.onSpeaking != nil {
|
||||
r.onSpeaking(id, false)
|
||||
}
|
||||
delete(timelines, id)
|
||||
}
|
||||
}
|
||||
if cmd.done != nil {
|
||||
close(cmd.done)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *receiveAudio) tick(timelines map[int]*receiveTimeline, now time.Time) {
|
||||
mix := make([]int32, receiveFrameInterleavedSamples)
|
||||
for id, t := range timelines {
|
||||
if now.Sub(t.lastPacket) > 2*time.Second {
|
||||
t.release()
|
||||
if t.speaking && r.onSpeaking != nil {
|
||||
r.onSpeaking(id, false)
|
||||
}
|
||||
delete(timelines, id)
|
||||
continue
|
||||
}
|
||||
pcm, active := t.next()
|
||||
if active {
|
||||
count := atomic.AddUint64(&r.decoded, 1)
|
||||
if count <= 5 || count%250 == 0 {
|
||||
log.Printf("[TSVoice] decoded count=%d client=%d", count, id)
|
||||
}
|
||||
if t.lastReal {
|
||||
t.lastActive = now
|
||||
if !t.speaking {
|
||||
t.speaking = true
|
||||
if r.onSpeaking != nil {
|
||||
r.onSpeaking(id, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if t.speaking && now.Sub(t.lastActive) > 400*time.Millisecond {
|
||||
t.speaking = false
|
||||
if r.onSpeaking != nil {
|
||||
r.onSpeaking(id, false)
|
||||
}
|
||||
}
|
||||
if !t.muted && len(pcm) == receiveFrameInterleavedSamples {
|
||||
for i, sample := range pcm {
|
||||
mix[i] += int32(sample)
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.onPCM != nil && len(timelines) > 0 {
|
||||
count := atomic.AddUint64(&r.mixed, 1)
|
||||
if count <= 5 || count%250 == 0 {
|
||||
log.Printf("[TSVoice] mixed count=%d clients=%d", count, len(timelines))
|
||||
}
|
||||
r.onPCM(mixPCM16(mix))
|
||||
}
|
||||
}
|
||||
|
||||
var errBadFrame = errors.New("voice decoder returned a non-20ms frame")
|
||||
|
||||
func voiceCodecChannels(codec int) int {
|
||||
if codec == 5 {
|
||||
return receiveChannels
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func normalizeDecodedPCM(pcm []int16, channels int) []int16 {
|
||||
if channels == receiveChannels {
|
||||
return pcm
|
||||
}
|
||||
return monoToStereo(pcm)
|
||||
}
|
||||
|
||||
func monoToStereo(mono []int16) []int16 {
|
||||
stereo := make([]int16, len(mono)*receiveChannels)
|
||||
for i, sample := range mono {
|
||||
stereo[i*receiveChannels], stereo[i*receiveChannels+1] = sample, sample
|
||||
}
|
||||
return stereo
|
||||
}
|
||||
Reference in New Issue
Block a user