234 lines
5.7 KiB
Go
234 lines
5.7 KiB
Go
// +build !amd64,!386,cgo
|
|
|
|
package gopus
|
|
|
|
// #cgo !nopkgconfig pkg-config: opus
|
|
//
|
|
// #include <opus.h>
|
|
// enum {
|
|
// gopus_ok = OPUS_OK,
|
|
// gopus_bad_arg = OPUS_BAD_ARG,
|
|
// gopus_small_buffer = OPUS_BUFFER_TOO_SMALL,
|
|
// gopus_internal = OPUS_INTERNAL_ERROR,
|
|
// gopus_invalid_packet = OPUS_INVALID_PACKET,
|
|
// gopus_unimplemented = OPUS_UNIMPLEMENTED,
|
|
// gopus_invalid_state = OPUS_INVALID_STATE,
|
|
// gopus_alloc_fail = OPUS_ALLOC_FAIL,
|
|
// };
|
|
//
|
|
//
|
|
// enum {
|
|
// gopus_application_voip = OPUS_APPLICATION_VOIP,
|
|
// gopus_application_audio = OPUS_APPLICATION_AUDIO,
|
|
// gopus_restricted_lowdelay = OPUS_APPLICATION_RESTRICTED_LOWDELAY,
|
|
// gopus_bitrate_max = OPUS_BITRATE_MAX,
|
|
// };
|
|
//
|
|
//
|
|
// void gopus_setvbr(OpusEncoder *encoder, int vbr) {
|
|
// opus_encoder_ctl(encoder, OPUS_SET_VBR(vbr));
|
|
// }
|
|
//
|
|
// void gopus_setbitrate(OpusEncoder *encoder, int bitrate) {
|
|
// opus_encoder_ctl(encoder, OPUS_SET_BITRATE(bitrate));
|
|
// }
|
|
//
|
|
// opus_int32 gopus_bitrate(OpusEncoder *encoder) {
|
|
// opus_int32 bitrate;
|
|
// opus_encoder_ctl(encoder, OPUS_GET_BITRATE(&bitrate));
|
|
// return bitrate;
|
|
// }
|
|
//
|
|
// void gopus_setapplication(OpusEncoder *encoder, int application) {
|
|
// opus_encoder_ctl(encoder, OPUS_SET_APPLICATION(application));
|
|
// }
|
|
//
|
|
// opus_int32 gopus_application(OpusEncoder *encoder) {
|
|
// opus_int32 application;
|
|
// opus_encoder_ctl(encoder, OPUS_GET_APPLICATION(&application));
|
|
// return application;
|
|
// }
|
|
//
|
|
// void gopus_encoder_resetstate(OpusEncoder *encoder) {
|
|
// opus_encoder_ctl(encoder, OPUS_RESET_STATE);
|
|
// }
|
|
//
|
|
// void gopus_decoder_resetstate(OpusDecoder *decoder) {
|
|
// opus_decoder_ctl(decoder, OPUS_RESET_STATE);
|
|
// }
|
|
import "C"
|
|
|
|
import (
|
|
"errors"
|
|
"unsafe"
|
|
)
|
|
|
|
type Application int
|
|
|
|
const (
|
|
Voip Application = C.gopus_application_voip
|
|
Audio Application = C.gopus_application_audio
|
|
RestrictedLowDelay Application = C.gopus_restricted_lowdelay
|
|
)
|
|
|
|
const (
|
|
BitrateMaximum = C.gopus_bitrate_max
|
|
)
|
|
|
|
type Encoder struct {
|
|
data []byte
|
|
cEncoder *C.struct_OpusEncoder
|
|
}
|
|
|
|
func NewEncoder(sampleRate, channels int, application Application) (*Encoder, error) {
|
|
encoder := &Encoder{}
|
|
encoder.data = make([]byte, int(C.opus_encoder_get_size(C.int(channels))))
|
|
encoder.cEncoder = (*C.struct_OpusEncoder)(unsafe.Pointer(&encoder.data[0]))
|
|
|
|
ret := C.opus_encoder_init(encoder.cEncoder, C.opus_int32(sampleRate), C.int(channels), C.int(application))
|
|
if err := getErr(ret); err != nil {
|
|
return nil, err
|
|
}
|
|
return encoder, nil
|
|
}
|
|
|
|
func (e *Encoder) Encode(pcm []int16, frameSize, maxDataBytes int) ([]byte, error) {
|
|
pcmPtr := (*C.opus_int16)(unsafe.Pointer(&pcm[0]))
|
|
|
|
data := make([]byte, maxDataBytes)
|
|
dataPtr := (*C.uchar)(unsafe.Pointer(&data[0]))
|
|
|
|
encodedC := C.opus_encode(e.cEncoder, pcmPtr, C.int(frameSize), dataPtr, C.opus_int32(len(data)))
|
|
encoded := int(encodedC)
|
|
|
|
if encoded < 0 {
|
|
return nil, getErr(C.int(encodedC))
|
|
}
|
|
return data[0:encoded], nil
|
|
}
|
|
|
|
func (e *Encoder) SetVbr(vbr bool) {
|
|
var cVbr C.int
|
|
if vbr {
|
|
cVbr = 1
|
|
} else {
|
|
cVbr = 0
|
|
}
|
|
C.gopus_setvbr(e.cEncoder, cVbr)
|
|
}
|
|
|
|
func (e *Encoder) SetBitrate(bitrate int) {
|
|
C.gopus_setbitrate(e.cEncoder, C.int(bitrate))
|
|
}
|
|
|
|
func (e *Encoder) Bitrate() int {
|
|
return int(C.gopus_bitrate(e.cEncoder))
|
|
}
|
|
|
|
func (e *Encoder) SetApplication(application Application) {
|
|
C.gopus_setapplication(e.cEncoder, C.int(application))
|
|
}
|
|
|
|
func (e *Encoder) Application() Application {
|
|
return Application(C.gopus_application(e.cEncoder))
|
|
}
|
|
|
|
func (e *Encoder) ResetState() {
|
|
C.gopus_encoder_resetstate(e.cEncoder)
|
|
}
|
|
|
|
type Decoder struct {
|
|
data []byte
|
|
cDecoder *C.struct_OpusDecoder
|
|
channels int
|
|
}
|
|
|
|
func NewDecoder(sampleRate, channels int) (*Decoder, error) {
|
|
decoder := &Decoder{}
|
|
decoder.data = make([]byte, int(C.opus_decoder_get_size(C.int(channels))))
|
|
decoder.cDecoder = (*C.struct_OpusDecoder)(unsafe.Pointer(&decoder.data[0]))
|
|
|
|
ret := C.opus_decoder_init(decoder.cDecoder, C.opus_int32(sampleRate), C.int(channels))
|
|
if err := getErr(ret); err != nil {
|
|
return nil, err
|
|
}
|
|
decoder.channels = channels
|
|
|
|
return decoder, nil
|
|
}
|
|
|
|
func (d *Decoder) Decode(data []byte, frameSize int, fec bool) ([]int16, error) {
|
|
var dataPtr *C.uchar
|
|
if len(data) > 0 {
|
|
dataPtr = (*C.uchar)(unsafe.Pointer(&data[0]))
|
|
}
|
|
dataLen := C.opus_int32(len(data))
|
|
|
|
output := make([]int16, d.channels*frameSize)
|
|
outputPtr := (*C.opus_int16)(unsafe.Pointer(&output[0]))
|
|
|
|
var cFec C.int
|
|
if fec {
|
|
cFec = 1
|
|
} else {
|
|
cFec = 0
|
|
}
|
|
|
|
cRet := C.opus_decode(d.cDecoder, dataPtr, dataLen, outputPtr, C.int(frameSize), cFec)
|
|
ret := int(cRet)
|
|
|
|
if ret < 0 {
|
|
return nil, getErr(cRet)
|
|
}
|
|
return output[:ret*d.channels], nil
|
|
}
|
|
|
|
func (d *Decoder) ResetState() {
|
|
C.gopus_decoder_resetstate(d.cDecoder)
|
|
}
|
|
|
|
func CountFrames(data []byte) (int, error) {
|
|
dataPtr := (*C.uchar)(unsafe.Pointer(&data[0]))
|
|
cLen := C.opus_int32(len(data))
|
|
|
|
cRet := C.opus_packet_get_nb_frames(dataPtr, cLen)
|
|
if err := getErr(cRet); err != nil {
|
|
return 0, err
|
|
}
|
|
return int(cRet), nil
|
|
}
|
|
|
|
var (
|
|
ErrBadArgument = errors.New("bad argument")
|
|
ErrSmallBuffer = errors.New("buffer is too small")
|
|
ErrInternal = errors.New("internal error")
|
|
ErrInvalidPacket = errors.New("invalid packet")
|
|
ErrUnimplemented = errors.New("unimplemented")
|
|
ErrInvalidState = errors.New("invalid state")
|
|
ErrAllocFail = errors.New("allocation failed")
|
|
ErrUnknown = errors.New("unknown error")
|
|
)
|
|
|
|
func getErr(code C.int) error {
|
|
switch code {
|
|
case C.gopus_ok:
|
|
return nil
|
|
case C.gopus_bad_arg:
|
|
return ErrBadArgument
|
|
case C.gopus_small_buffer:
|
|
return ErrSmallBuffer
|
|
case C.gopus_internal:
|
|
return ErrInternal
|
|
case C.gopus_invalid_packet:
|
|
return ErrInvalidPacket
|
|
case C.gopus_unimplemented:
|
|
return ErrUnimplemented
|
|
case C.gopus_invalid_state:
|
|
return ErrInvalidState
|
|
case C.gopus_alloc_fail:
|
|
return ErrAllocFail
|
|
default:
|
|
return ErrUnknown
|
|
}
|
|
}
|