Files
mediadevices/pkg/codec/opus/opus.go
Valentin Cocaud 524bf8faad Add an interface for easier customization of the EncoderController
Making the ReadCloser an Controllable allows to uncouple
the controller implementation from the encoder.
This is not needed for the 2 codec controller already implemented (openh264 and vpx)
but is more future proof in case it required for other codecs.
2022-07-01 17:26:55 +02:00

141 lines
2.8 KiB
Go

package opus
import (
"errors"
"fmt"
"github.com/pion/mediadevices/pkg/codec"
"github.com/pion/mediadevices/pkg/io/audio"
"github.com/pion/mediadevices/pkg/prop"
"github.com/pion/mediadevices/pkg/wave"
"github.com/pion/mediadevices/pkg/wave/mixer"
)
/*
#include <opus.h>
int bridge_encoder_set_bitrate(OpusEncoder *e, opus_int32 bitrate)
{
return opus_encoder_ctl(e, OPUS_SET_BITRATE(bitrate));
}
*/
import "C"
type encoder struct {
inBuff wave.Audio
reader audio.Reader
engine *C.OpusEncoder
}
func newEncoder(r audio.Reader, p prop.Media, params Params) (codec.ReadCloser, error) {
var cerror C.int
if p.SampleRate == 0 {
return nil, fmt.Errorf("opus: inProp.SampleRate is required")
}
if params.BitRate == 0 {
params.BitRate = 32000
}
if params.ChannelMixer == nil {
params.ChannelMixer = &mixer.MonoMixer{}
}
if !params.Latency.Validate() {
return nil, fmt.Errorf("opus: unsupported latency %v", params.Latency)
}
channels := p.ChannelCount
engine := C.opus_encoder_create(
C.opus_int32(p.SampleRate),
C.int(channels),
C.OPUS_APPLICATION_VOIP,
&cerror,
)
if cerror != C.OPUS_OK {
return nil, errors.New("failed to create encoder engine")
}
rMix := audio.NewChannelMixer(channels, params.ChannelMixer)
rBuf := audio.NewBuffer(params.Latency.samples(p.SampleRate))
e := encoder{
engine: engine,
reader: rMix(rBuf(r)),
}
err := e.SetBitRate(params.BitRate)
if err != nil {
e.Close()
return nil, err
}
return &e, nil
}
func (e *encoder) Read() ([]byte, func(), error) {
buff, _, err := e.reader.Read()
if err != nil {
return nil, func() {}, err
}
encoded := make([]byte, 1024)
var n C.opus_int32
switch b := buff.(type) {
case *wave.Int16Interleaved:
n = C.opus_encode(
e.engine,
(*C.opus_int16)(&b.Data[0]),
C.int(b.ChunkInfo().Len),
(*C.uchar)(&encoded[0]),
C.opus_int32(cap(encoded)),
)
case *wave.Float32Interleaved:
n = C.opus_encode_float(
e.engine,
(*C.float)(&b.Data[0]),
C.int(b.ChunkInfo().Len),
(*C.uchar)(&encoded[0]),
C.opus_int32(cap(encoded)),
)
default:
err = errors.New("unknown type of audio buffer")
}
if n < 0 {
err = errors.New("failed to encode")
}
return encoded[:n:n], func() {}, err
}
var _ codec.BitRateController = (*encoder)(nil)
func (e *encoder) SetBitRate(bitRate int) error {
cerror := C.bridge_encoder_set_bitrate(
e.engine,
C.int(bitRate),
)
if cerror != C.OPUS_OK {
return fmt.Errorf("failed to set encoder's bitrate to %d", bitRate)
}
return nil
}
// TODO: Implement key frame controller
//var _ codec.KeyFrameController = (*encoder)(nil)
func (e *encoder) Controller() codec.EncoderController {
return e
}
func (e *encoder) Close() error {
if e.engine == nil {
return nil
}
C.opus_encoder_destroy(e.engine)
e.engine = nil
return nil
}