398 lines
10 KiB
Go
398 lines
10 KiB
Go
// Copyright © Go Opus Authors (see AUTHORS file)
|
|
//
|
|
// License for use of this code is detailed in the LICENSE file
|
|
|
|
package opus
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"runtime/cgo"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
/*
|
|
#cgo pkg-config: opus libopusenc
|
|
#include <opus.h>
|
|
#include <opusenc.h>
|
|
|
|
int
|
|
bridge_encoder_set_dtx(OggOpusEnc *st, opus_int32 use_dtx);
|
|
|
|
int
|
|
bridge_encoder_get_dtx(OggOpusEnc *st, opus_int32 *dtx);
|
|
|
|
int
|
|
bridge_encoder_get_sample_rate(OggOpusEnc *st, opus_int32 *sample_rate);
|
|
|
|
int
|
|
bridge_encoder_set_muxing_delay(OggOpusEnc *st, opus_int32 delay);
|
|
|
|
int
|
|
bridge_encoder_get_muxing_delay(OggOpusEnc *st, opus_int32 *delay);
|
|
|
|
int
|
|
bridge_encoder_set_bitrate(OggOpusEnc *st, opus_int32 bitrate);
|
|
|
|
int
|
|
bridge_encoder_get_bitrate(OggOpusEnc *st, opus_int32 *bitrate);
|
|
|
|
int
|
|
bridge_encoder_set_complexity(OggOpusEnc *st, opus_int32 complexity);
|
|
|
|
int
|
|
bridge_encoder_get_complexity(OggOpusEnc *st, opus_int32 *complexity);
|
|
|
|
int
|
|
bridge_encoder_set_max_bandwidth(OggOpusEnc *st, opus_int32 max_bw);
|
|
|
|
int
|
|
bridge_encoder_get_max_bandwidth(OggOpusEnc *st, opus_int32 *max_bw);
|
|
|
|
int
|
|
bridge_encoder_set_inband_fec(OggOpusEnc *st, opus_int32 fec);
|
|
|
|
int
|
|
bridge_encoder_get_inband_fec(OggOpusEnc *st, opus_int32 *fec);
|
|
|
|
int
|
|
bridge_encoder_set_packet_loss_perc(OggOpusEnc *st, opus_int32 loss_perc);
|
|
|
|
int
|
|
bridge_encoder_get_packet_loss_perc(OggOpusEnc *st, opus_int32 *loss_perc);
|
|
|
|
int
|
|
bridge_encoder_set_application(OggOpusEnc *st, opus_int32 application);
|
|
|
|
OpusEncCallbacks * my_write_callbacks();
|
|
|
|
OggOpusComments* my_empty_comment();
|
|
|
|
*/
|
|
import "C"
|
|
|
|
type Bandwidth int
|
|
|
|
const (
|
|
// 4 kHz passband
|
|
Narrowband = Bandwidth(C.OPUS_BANDWIDTH_NARROWBAND)
|
|
// 6 kHz passband
|
|
Mediumband = Bandwidth(C.OPUS_BANDWIDTH_MEDIUMBAND)
|
|
// 8 kHz passband
|
|
Wideband = Bandwidth(C.OPUS_BANDWIDTH_WIDEBAND)
|
|
// 12 kHz passband
|
|
SuperWideband = Bandwidth(C.OPUS_BANDWIDTH_SUPERWIDEBAND)
|
|
// 20 kHz passband
|
|
Fullband = Bandwidth(C.OPUS_BANDWIDTH_FULLBAND)
|
|
)
|
|
|
|
var errEncUninitialized = fmt.Errorf("opus encoder uninitialized")
|
|
|
|
// Encoder contains the state of an Opus encoder for libopus.
|
|
type Encoder struct {
|
|
p *C.struct_OggOpusEnc
|
|
channels int
|
|
handle cgo.Handle
|
|
writer io.Writer
|
|
}
|
|
|
|
//export go_writecallback
|
|
func go_writecallback(p unsafe.Pointer, cbuf *C.uchar, length C.int) C.int {
|
|
enc := ((*cgo.Handle)(p)).Value().(*Encoder)
|
|
if enc == nil {
|
|
// This is bad
|
|
return 1
|
|
}
|
|
|
|
_, err := enc.writer.Write(unsafe.Slice((*byte)(cbuf), int(length)))
|
|
|
|
if err != nil {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// NewEncoder allocates a new Opus encoder and initializes it with the appropriate parameters.
|
|
func NewEncoder(sampleRate int, channels int, application Application, writer io.Writer) (*Encoder, error) {
|
|
var enc Encoder
|
|
err := enc.Init(sampleRate, channels, application, writer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &enc, nil
|
|
}
|
|
|
|
// Init initializes a pre-allocated opus encoder. Unless the encoder has been
|
|
// created using NewEncoder, this method must be called exactly once in the
|
|
// life-time of this object, before calling any other methods.
|
|
func (enc *Encoder) Init(sampleRate int, channels int, application Application, writer io.Writer) error {
|
|
if enc.p != nil {
|
|
return fmt.Errorf("opus encoder already initialized")
|
|
}
|
|
if channels != 1 && channels != 2 {
|
|
return fmt.Errorf("number of channels must be 1 or 2: %d", channels)
|
|
}
|
|
|
|
enc.channels = channels
|
|
enc.writer = writer
|
|
enc.handle = cgo.NewHandle(enc)
|
|
|
|
var errno C.int
|
|
if channels > 8 {
|
|
enc.p = C.ope_encoder_create_callbacks(C.my_write_callbacks(), unsafe.Pointer(&enc.handle), C.my_empty_comment(), C.opus_int32(sampleRate), C.int(channels), C.int(255), &errno)
|
|
} else if channels > 2 {
|
|
enc.p = C.ope_encoder_create_callbacks(C.my_write_callbacks(), unsafe.Pointer(&enc.handle), C.my_empty_comment(), C.opus_int32(sampleRate), C.int(channels), C.int(1), &errno)
|
|
} else {
|
|
enc.p = C.ope_encoder_create_callbacks(C.my_write_callbacks(), unsafe.Pointer(&enc.handle), C.my_empty_comment(), C.opus_int32(sampleRate), C.int(channels), C.int(0), &errno)
|
|
}
|
|
|
|
if errno != 0 {
|
|
return Error(int(errno))
|
|
}
|
|
C.bridge_encoder_set_application(enc.p, C.int(application))
|
|
return nil
|
|
}
|
|
|
|
// Encode raw PCM data
|
|
func (enc *Encoder) Encode(pcm []int16) error {
|
|
if enc.p == nil {
|
|
return errEncUninitialized
|
|
}
|
|
if len(pcm) == 0 {
|
|
return fmt.Errorf("opus: no data supplied")
|
|
}
|
|
|
|
if len(pcm)%enc.channels != 0 {
|
|
return fmt.Errorf("opus: input buffer length must be multiple of channels")
|
|
}
|
|
samples := len(pcm) / enc.channels
|
|
|
|
n := int(C.ope_encoder_write(enc.p, (*C.opus_int16)(&pcm[0]), C.int(samples)))
|
|
if n != 0 {
|
|
return Error(n)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FlushHeaders Write out the header now rather than wait for audio to begin.
|
|
func (enc *Encoder) FlushHeaders() error {
|
|
res := C.ope_encoder_flush_header(enc.p)
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Close finishes stream, then frees allocated resources
|
|
func (enc *Encoder) Close() error {
|
|
if enc.p != nil {
|
|
res := C.ope_encoder_drain(enc.p)
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
C.ope_encoder_destroy(enc.p)
|
|
enc.p = nil
|
|
enc.handle.Delete()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EncodeFloat32 raw PCM data
|
|
func (enc *Encoder) EncodeFloat32(pcm []float32) error {
|
|
if enc.p == nil {
|
|
return errEncUninitialized
|
|
}
|
|
if len(pcm) == 0 {
|
|
return fmt.Errorf("opus: no data supplied")
|
|
}
|
|
|
|
if len(pcm)%enc.channels != 0 {
|
|
return fmt.Errorf("opus: input buffer length must be multiple of channels")
|
|
}
|
|
samples := len(pcm) / enc.channels
|
|
|
|
n := int(C.ope_encoder_write_float(enc.p, (*C.float)(&pcm[0]), C.int(samples)))
|
|
if n != 0 {
|
|
return Error(n)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetDTX configures the encoder's use of discontinuous transmission (DTX).
|
|
func (enc *Encoder) SetDTX(dtx bool) error {
|
|
i := 0
|
|
if dtx {
|
|
i = 1
|
|
}
|
|
res := C.bridge_encoder_set_dtx(enc.p, C.opus_int32(i))
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DTX reports whether this encoder is configured to use discontinuous
|
|
// transmission (DTX).
|
|
func (enc *Encoder) DTX() (bool, error) {
|
|
var dtx C.opus_int32
|
|
res := C.bridge_encoder_get_dtx(enc.p, &dtx)
|
|
if res != C.OPUS_OK {
|
|
return false, Error(res)
|
|
}
|
|
return dtx != 0, nil
|
|
}
|
|
|
|
// SampleRate returns the encoder sample rate in Hz.
|
|
func (enc *Encoder) SampleRate() (int, error) {
|
|
var sr C.opus_int32
|
|
res := C.bridge_encoder_get_sample_rate(enc.p, &sr)
|
|
if res != C.OPUS_OK {
|
|
return 0, Error(res)
|
|
}
|
|
return int(sr), nil
|
|
}
|
|
|
|
// SetBitrate sets the bitrate of the Encoder
|
|
func (enc *Encoder) SetBitrate(bitrate int) error {
|
|
res := C.bridge_encoder_set_bitrate(enc.p, C.opus_int32(bitrate))
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetBitrateToAuto will allow the encoder to automatically set the bitrate
|
|
func (enc *Encoder) SetBitrateToAuto() error {
|
|
res := C.bridge_encoder_set_bitrate(enc.p, C.opus_int32(C.OPUS_AUTO))
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetBitrateToMax causes the encoder to use as much rate as it can. This can be
|
|
// useful for controlling the rate by adjusting the output buffer size.
|
|
func (enc *Encoder) SetBitrateToMax() error {
|
|
res := C.bridge_encoder_set_bitrate(enc.p, C.opus_int32(C.OPUS_BITRATE_MAX))
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Bitrate returns the bitrate of the Encoder
|
|
func (enc *Encoder) Bitrate() (int, error) {
|
|
var bitrate C.opus_int32
|
|
res := C.bridge_encoder_get_bitrate(enc.p, &bitrate)
|
|
if res != C.OPUS_OK {
|
|
return 0, Error(res)
|
|
}
|
|
return int(bitrate), nil
|
|
}
|
|
|
|
// SetMuxingDelay sets maximum container delay
|
|
func (enc *Encoder) SetMuxingDelay(delay time.Duration) error {
|
|
if delay < 0 || delay > time.Millisecond*1000 {
|
|
return errors.New("value must be between 0 to 1000 milliseconds")
|
|
}
|
|
res := C.bridge_encoder_set_muxing_delay(enc.p, C.opus_int32(delay.Milliseconds()*48))
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// MuxingDelay returns the maximum container delay
|
|
func (enc *Encoder) MuxingDelay() (time.Duration, error) {
|
|
var delay C.opus_int32
|
|
res := C.bridge_encoder_get_muxing_delay(enc.p, &delay)
|
|
if res != C.OPUS_OK {
|
|
return 0, Error(res)
|
|
}
|
|
return time.Millisecond * time.Duration(int(delay)/48), nil
|
|
}
|
|
|
|
// SetComplexity sets the encoder's computational complexity
|
|
func (enc *Encoder) SetComplexity(complexity int) error {
|
|
res := C.bridge_encoder_set_complexity(enc.p, C.opus_int32(complexity))
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Complexity returns the computational complexity used by the encoder
|
|
func (enc *Encoder) Complexity() (int, error) {
|
|
var complexity C.opus_int32
|
|
res := C.bridge_encoder_get_complexity(enc.p, &complexity)
|
|
if res != C.OPUS_OK {
|
|
return 0, Error(res)
|
|
}
|
|
return int(complexity), nil
|
|
}
|
|
|
|
// SetMaxBandwidth configures the maximum bandpass that the encoder will select
|
|
// automatically
|
|
func (enc *Encoder) SetMaxBandwidth(maxBw Bandwidth) error {
|
|
res := C.bridge_encoder_set_max_bandwidth(enc.p, C.opus_int32(maxBw))
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// MaxBandwidth gets the encoder's configured maximum allowed bandpass.
|
|
func (enc *Encoder) MaxBandwidth() (Bandwidth, error) {
|
|
var maxBw C.opus_int32
|
|
res := C.bridge_encoder_get_max_bandwidth(enc.p, &maxBw)
|
|
if res != C.OPUS_OK {
|
|
return 0, Error(res)
|
|
}
|
|
return Bandwidth(maxBw), nil
|
|
}
|
|
|
|
// SetInBandFEC configures the encoder's use of inband forward error
|
|
// correction (FEC)
|
|
func (enc *Encoder) SetInBandFEC(fec bool) error {
|
|
i := 0
|
|
if fec {
|
|
i = 1
|
|
}
|
|
res := C.bridge_encoder_set_inband_fec(enc.p, C.opus_int32(i))
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// InBandFEC gets the encoder's configured inband forward error correction (FEC)
|
|
func (enc *Encoder) InBandFEC() (bool, error) {
|
|
var fec C.opus_int32
|
|
res := C.bridge_encoder_get_inband_fec(enc.p, &fec)
|
|
if res != C.OPUS_OK {
|
|
return false, Error(res)
|
|
}
|
|
return fec != 0, nil
|
|
}
|
|
|
|
// SetPacketLossPerc configures the encoder's expected packet loss percentage.
|
|
func (enc *Encoder) SetPacketLossPerc(lossPerc int) error {
|
|
res := C.bridge_encoder_set_packet_loss_perc(enc.p, C.opus_int32(lossPerc))
|
|
if res != C.OPUS_OK {
|
|
return Error(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PacketLossPerc gets the encoder's configured packet loss percentage.
|
|
func (enc *Encoder) PacketLossPerc() (int, error) {
|
|
var lossPerc C.opus_int32
|
|
res := C.bridge_encoder_get_packet_loss_perc(enc.p, &lossPerc)
|
|
if res != C.OPUS_OK {
|
|
return 0, Error(res)
|
|
}
|
|
return int(lossPerc), nil
|
|
}
|