mirror of
https://github.com/pion/mediadevices.git
synced 2025-10-12 20:11:13 +08:00

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.
141 lines
2.8 KiB
Go
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
|
|
}
|