110 lines
3.0 KiB
Go
110 lines
3.0 KiB
Go
//go:build android
|
|||
|
|
|
||
|
|
package teamspeak
|
||
|
|
|
||
|
|
/*
|
||
|
|
#cgo CFLAGS: -I${SRCDIR}/../../android/app/src/main/cpp/third_party/opus/include
|
||
|
|
#cgo android,arm64 LDFLAGS: -L${SRCDIR}/.opus/lib/arm64-v8a -l:libopus.a -lm
|
||
|
|
#cgo android,arm LDFLAGS: -L${SRCDIR}/.opus/lib/armeabi-v7a -l:libopus.a -lm
|
||
|
|
#cgo android,386 LDFLAGS: -L${SRCDIR}/.opus/lib/x86 -l:libopus.a -lm
|
||
|
|
#cgo android,amd64 LDFLAGS: -L${SRCDIR}/.opus/lib/x86_64 -l:libopus.a -lm
|
||
|
|
#include "opus.h"
|
||
|
|
#include <stdlib.h>
|
||
|
|
*/
|
||
|
|
import "C"
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"runtime"
|
||
|
|
"sync"
|
||
|
|
"unsafe"
|
||
|
|
)
|
||
|
|
|
||
|
|
var errOpusDecoderReleased = errors.New("opus decoder has been released")
|
||
|
|
|
||
|
|
type opusVoiceDecoderFactory struct{}
|
||
|
|
|
||
|
|
type opusVoiceDecoder struct {
|
||
|
|
mu sync.Mutex
|
||
|
|
decoder *C.OpusDecoder
|
||
|
|
channels int
|
||
|
|
}
|
||
|
|
|
||
|
|
func newPlatformVoiceDecoderFactory() voiceDecoderFactory { return opusVoiceDecoderFactory{} }
|
||
|
|
|
||
|
|
func (opusVoiceDecoderFactory) NewvoiceDecoder(_ int, codec int) (voiceDecoder, error) {
|
||
|
|
if codec != 4 && codec != 5 {
|
||
|
|
return nil, fmt.Errorf("unsupported TeamSpeak voice codec %d", codec)
|
||
|
|
}
|
||
|
|
channels := voiceCodecChannels(codec)
|
||
|
|
var opusErr C.int
|
||
|
|
decoder := C.opus_decoder_create(C.opus_int32(receiveSampleRate), C.int(channels), &opusErr)
|
||
|
|
if decoder == nil || opusErr != C.OPUS_OK {
|
||
|
|
return nil, fmt.Errorf("opus_decoder_create: %d", int(opusErr))
|
||
|
|
}
|
||
|
|
return &opusVoiceDecoder{decoder: decoder, channels: channels}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (d *opusVoiceDecoder) Decode(packet []byte) ([]int16, error) {
|
||
|
|
if len(packet) == 0 {
|
||
|
|
return nil, errors.New("empty Opus packet")
|
||
|
|
}
|
||
|
|
d.mu.Lock()
|
||
|
|
defer d.mu.Unlock()
|
||
|
|
if d.decoder == nil {
|
||
|
|
return nil, errOpusDecoderReleased
|
||
|
|
}
|
||
|
|
pcm := make([]int16, receiveMaxDecodeSamplesPerChannel*d.channels)
|
||
|
|
decoded := C.opus_decode(
|
||
|
|
d.decoder,
|
||
|
|
(*C.uchar)(unsafe.Pointer(&packet[0])),
|
||
|
|
C.opus_int32(len(packet)),
|
||
|
|
(*C.opus_int16)(unsafe.Pointer(&pcm[0])),
|
||
|
|
C.int(receiveMaxDecodeSamplesPerChannel),
|
||
|
|
0,
|
||
|
|
)
|
||
|
|
runtime.KeepAlive(packet)
|
||
|
|
return decodedPCM(pcm, decoded, d.channels)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (d *opusVoiceDecoder) DecodeLost() ([]int16, error) {
|
||
|
|
d.mu.Lock()
|
||
|
|
defer d.mu.Unlock()
|
||
|
|
if d.decoder == nil {
|
||
|
|
return nil, errOpusDecoderReleased
|
||
|
|
}
|
||
|
|
pcm := make([]int16, receiveMaxDecodeSamplesPerChannel*d.channels)
|
||
|
|
decoded := C.opus_decode(
|
||
|
|
d.decoder,
|
||
|
|
nil,
|
||
|
|
0,
|
||
|
|
(*C.opus_int16)(unsafe.Pointer(&pcm[0])),
|
||
|
|
C.int(receiveMaxDecodeSamplesPerChannel),
|
||
|
|
0,
|
||
|
|
)
|
||
|
|
return decodedPCM(pcm, decoded, d.channels)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (d *opusVoiceDecoder) Release() {
|
||
|
|
d.mu.Lock()
|
||
|
|
decoder := d.decoder
|
||
|
|
d.decoder = nil
|
||
|
|
d.mu.Unlock()
|
||
|
|
if decoder != nil {
|
||
|
|
C.opus_decoder_destroy(decoder)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func decodedPCM(pcm []int16, decoded C.int, channels int) ([]int16, error) {
|
||
|
|
if decoded < 0 {
|
||
|
|
return nil, fmt.Errorf("opus_decode: %d", int(decoded))
|
||
|
|
}
|
||
|
|
perChannel := int(decoded)
|
||
|
|
decodedSamples := perChannel * channels
|
||
|
|
if perChannel > receiveMaxDecodeSamplesPerChannel || decodedSamples > len(pcm) {
|
||
|
|
return nil, fmt.Errorf("opus_decode returned %d samples/channel, buffer has %d samples for %d channels", perChannel, len(pcm), channels)
|
||
|
|
}
|
||
|
|
return normalizeDecodedPCM(pcm[:decodedSamples], channels), nil
|
||
|
|
}
|