go-pus/encoder.go
DataHoarder 4421c708d1
Some checks failed
continuous-integration/drone/push Build is failing
Add support for setting and getting the ogg muxing delay
2022-07-20 12:04:58 +02:00

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
}