Use libopusenc to encode stream, WiP: tests
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
c9b07c6bec
commit
6cc027f24d
109
callbacks.c
109
callbacks.c
|
@ -9,15 +9,28 @@
|
|||
// plays nice with the CGo rules and avoids any confusion.
|
||||
|
||||
#include <opusfile.h>
|
||||
#include <opusenc.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Defined in Go. Uses the same signature as Go, no need for proxy function.
|
||||
int go_readcallback(void *p, unsigned char *buf, int nbytes);
|
||||
int go_writecallback(void *p, const unsigned char *buf, opus_int32 len);
|
||||
|
||||
int closecallback(void *p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct OpusFileCallbacks callbacks = {
|
||||
.read = go_readcallback,
|
||||
};
|
||||
|
||||
static OpusEncCallbacks encodeCallbacks = {
|
||||
.write = go_writecallback,
|
||||
.close = closecallback,
|
||||
};
|
||||
|
||||
static OggOpusComments* emptyComment = NULL;
|
||||
|
||||
// Proxy function for op_open_callbacks, because it takes a void * context but
|
||||
// we want to pass it non-pointer data, namely an arbitrary uintptr_t
|
||||
// value. This is legal C, but go test -race (-d=checkptr) complains anyway. So
|
||||
|
@ -28,3 +41,99 @@ my_open_callbacks(uintptr_t p, int *error)
|
|||
{
|
||||
return op_open_callbacks((void *)p, &callbacks, NULL, 0, error);
|
||||
}
|
||||
|
||||
OpusEncCallbacks *
|
||||
my_write_callbacks()
|
||||
{
|
||||
return &encodeCallbacks;
|
||||
}
|
||||
|
||||
OggOpusComments*
|
||||
my_empty_comment()
|
||||
{
|
||||
if (emptyComment == NULL) {
|
||||
emptyComment = ope_comments_create();
|
||||
}
|
||||
return emptyComment;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
bridge_encoder_set_dtx(OggOpusEnc *st, opus_int32 use_dtx)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_SET_DTX(use_dtx));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_dtx(OggOpusEnc *st, opus_int32 *dtx)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_GET_DTX(dtx));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_sample_rate(OggOpusEnc *st, opus_int32 *sample_rate)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_GET_SAMPLE_RATE(sample_rate));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
bridge_encoder_set_bitrate(OggOpusEnc *st, opus_int32 bitrate)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_bitrate(OggOpusEnc *st, opus_int32 *bitrate)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_GET_BITRATE(bitrate));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_set_complexity(OggOpusEnc *st, opus_int32 complexity)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_complexity(OggOpusEnc *st, opus_int32 *complexity)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_GET_COMPLEXITY(complexity));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_set_max_bandwidth(OggOpusEnc *st, opus_int32 max_bw)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_SET_MAX_BANDWIDTH(max_bw));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_max_bandwidth(OggOpusEnc *st, opus_int32 *max_bw)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_GET_MAX_BANDWIDTH(max_bw));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_set_inband_fec(OggOpusEnc *st, opus_int32 fec)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_SET_INBAND_FEC(fec));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_inband_fec(OggOpusEnc *st, opus_int32 *fec)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_GET_INBAND_FEC(fec));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_set_packet_loss_perc(OggOpusEnc *st, opus_int32 loss_perc)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(loss_perc));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_packet_loss_perc(OggOpusEnc *st, opus_int32 *loss_perc)
|
||||
{
|
||||
return ope_encoder_ctl(st, OPUS_GET_PACKET_LOSS_PERC(loss_perc));
|
||||
}
|
|
@ -92,7 +92,7 @@ func (dec *Decoder) Decode(data []byte, pcm []int16) (int, error) {
|
|||
return n, nil
|
||||
}
|
||||
|
||||
// Decode encoded Opus data into the supplied buffer. On success, returns the
|
||||
// DecodeFloat32 encoded Opus data into the supplied buffer. On success, returns the
|
||||
// number of samples correctly written to the target buffer.
|
||||
func (dec *Decoder) DecodeFloat32(data []byte, pcm []float32) (int, error) {
|
||||
if dec.p == nil {
|
||||
|
|
|
@ -30,39 +30,3 @@ func TestDecoderUnitialized(t *testing.T) {
|
|||
t.Errorf("Expected \"unitialized decoder\" error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoder_GetLastPacketDuration(t *testing.T) {
|
||||
const G4 = 391.995
|
||||
const SAMPLE_RATE = 48000
|
||||
const FRAME_SIZE_MS = 60
|
||||
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
|
||||
pcm := make([]int16, FRAME_SIZE)
|
||||
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
|
||||
if err != nil || enc == nil {
|
||||
t.Fatalf("Error creating new encoder: %v", err)
|
||||
}
|
||||
addSine(pcm, SAMPLE_RATE, G4)
|
||||
|
||||
data := make([]byte, 1000)
|
||||
n, err := enc.Encode(pcm, data)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't encode data: %v", err)
|
||||
}
|
||||
data = data[:n]
|
||||
|
||||
dec, err := NewDecoder(SAMPLE_RATE, 1)
|
||||
if err != nil || dec == nil {
|
||||
t.Fatalf("Error creating new decoder: %v", err)
|
||||
}
|
||||
n, err = dec.Decode(data, pcm)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
samples, err := dec.LastPacketDuration()
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't get last packet duration: %v", err)
|
||||
}
|
||||
if samples != n {
|
||||
t.Fatalf("Wrong duration length. Expected %d. Got %d", n, samples)
|
||||
}
|
||||
}
|
||||
|
|
211
encoder.go
211
encoder.go
|
@ -6,91 +6,60 @@ package opus
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime/cgo"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo pkg-config: opus
|
||||
#cgo pkg-config: opus libopusenc
|
||||
#include <opus.h>
|
||||
#include <opusenc.h>
|
||||
|
||||
int
|
||||
bridge_encoder_set_dtx(OpusEncoder *st, opus_int32 use_dtx)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_DTX(use_dtx));
|
||||
}
|
||||
bridge_encoder_set_dtx(OggOpusEnc *st, opus_int32 use_dtx);
|
||||
|
||||
int
|
||||
bridge_encoder_get_dtx(OpusEncoder *st, opus_int32 *dtx)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_DTX(dtx));
|
||||
}
|
||||
bridge_encoder_get_dtx(OggOpusEnc *st, opus_int32 *dtx);
|
||||
|
||||
int
|
||||
bridge_encoder_get_sample_rate(OpusEncoder *st, opus_int32 *sample_rate)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_SAMPLE_RATE(sample_rate));
|
||||
}
|
||||
bridge_encoder_get_sample_rate(OggOpusEnc *st, opus_int32 *sample_rate);
|
||||
|
||||
|
||||
int
|
||||
bridge_encoder_set_bitrate(OpusEncoder *st, opus_int32 bitrate)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
|
||||
}
|
||||
bridge_encoder_set_bitrate(OggOpusEnc *st, opus_int32 bitrate);
|
||||
|
||||
int
|
||||
bridge_encoder_get_bitrate(OpusEncoder *st, opus_int32 *bitrate)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_BITRATE(bitrate));
|
||||
}
|
||||
bridge_encoder_get_bitrate(OggOpusEnc *st, opus_int32 *bitrate);
|
||||
|
||||
int
|
||||
bridge_encoder_set_complexity(OpusEncoder *st, opus_int32 complexity)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
|
||||
}
|
||||
bridge_encoder_set_complexity(OggOpusEnc *st, opus_int32 complexity);
|
||||
|
||||
int
|
||||
bridge_encoder_get_complexity(OpusEncoder *st, opus_int32 *complexity)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_COMPLEXITY(complexity));
|
||||
}
|
||||
bridge_encoder_get_complexity(OggOpusEnc *st, opus_int32 *complexity);
|
||||
|
||||
int
|
||||
bridge_encoder_set_max_bandwidth(OpusEncoder *st, opus_int32 max_bw)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_MAX_BANDWIDTH(max_bw));
|
||||
}
|
||||
bridge_encoder_set_max_bandwidth(OggOpusEnc *st, opus_int32 max_bw);
|
||||
|
||||
int
|
||||
bridge_encoder_get_max_bandwidth(OpusEncoder *st, opus_int32 *max_bw)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_MAX_BANDWIDTH(max_bw));
|
||||
}
|
||||
bridge_encoder_get_max_bandwidth(OggOpusEnc *st, opus_int32 *max_bw);
|
||||
|
||||
int
|
||||
bridge_encoder_set_inband_fec(OpusEncoder *st, opus_int32 fec)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_INBAND_FEC(fec));
|
||||
}
|
||||
bridge_encoder_set_inband_fec(OggOpusEnc *st, opus_int32 fec);
|
||||
|
||||
int
|
||||
bridge_encoder_get_inband_fec(OpusEncoder *st, opus_int32 *fec)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_INBAND_FEC(fec));
|
||||
}
|
||||
bridge_encoder_get_inband_fec(OggOpusEnc *st, opus_int32 *fec);
|
||||
|
||||
int
|
||||
bridge_encoder_set_packet_loss_perc(OpusEncoder *st, opus_int32 loss_perc)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(loss_perc));
|
||||
}
|
||||
bridge_encoder_set_packet_loss_perc(OggOpusEnc *st, opus_int32 loss_perc);
|
||||
|
||||
int
|
||||
bridge_encoder_get_packet_loss_perc(OpusEncoder *st, opus_int32 *loss_perc)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_PACKET_LOSS_PERC(loss_perc));
|
||||
}
|
||||
bridge_encoder_get_packet_loss_perc(OggOpusEnc *st, opus_int32 *loss_perc);
|
||||
|
||||
|
||||
OpusEncCallbacks * my_write_callbacks();
|
||||
|
||||
OggOpusComments* my_empty_comment();
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
@ -114,18 +83,32 @@ var errEncUninitialized = fmt.Errorf("opus encoder uninitialized")
|
|||
|
||||
// Encoder contains the state of an Opus encoder for libopus.
|
||||
type Encoder struct {
|
||||
p *C.struct_OpusEncoder
|
||||
p *C.struct_OggOpusEnc
|
||||
channels int
|
||||
// Memory for the encoder struct allocated on the Go heap to allow Go GC to
|
||||
// manage it (and obviate need to free())
|
||||
mem []byte
|
||||
handle cgo.Handle
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// NewEncoder allocates a new Opus encoder and initializes it with the
|
||||
// appropriate parameters. All related memory is managed by the Go GC.
|
||||
func NewEncoder(sample_rate int, channels int, application Application) (*Encoder, error) {
|
||||
//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(sample_rate, channels, application)
|
||||
err := enc.Init(sampleRate, channels, application, writer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -135,84 +118,94 @@ func NewEncoder(sample_rate int, channels int, application Application) (*Encode
|
|||
// 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(sample_rate int, channels int, application Application) error {
|
||||
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)
|
||||
return fmt.Errorf("number of channels must be 1 or 2: %d", channels)
|
||||
}
|
||||
size := C.opus_encoder_get_size(C.int(channels))
|
||||
|
||||
enc.channels = channels
|
||||
enc.mem = make([]byte, size)
|
||||
enc.p = (*C.OpusEncoder)(unsafe.Pointer(&enc.mem[0]))
|
||||
errno := int(C.opus_encoder_init(
|
||||
enc.p,
|
||||
C.opus_int32(sample_rate),
|
||||
C.int(channels),
|
||||
C.int(application)))
|
||||
enc.writer = writer
|
||||
enc.handle = cgo.NewHandle(enc)
|
||||
|
||||
var errno C.int
|
||||
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))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode raw PCM data and store the result in the supplied buffer. On success,
|
||||
// returns the number of bytes used up by the encoded data.
|
||||
func (enc *Encoder) Encode(pcm []int16, data []byte) (int, error) {
|
||||
// Encode raw PCM data
|
||||
func (enc *Encoder) Encode(pcm []int16) error {
|
||||
if enc.p == nil {
|
||||
return 0, errEncUninitialized
|
||||
return errEncUninitialized
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return 0, fmt.Errorf("opus: no data supplied")
|
||||
return fmt.Errorf("opus: no data supplied")
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, fmt.Errorf("opus: no target buffer")
|
||||
}
|
||||
// libopus talks about samples as 1 sample containing multiple channels. So
|
||||
// e.g. 20 samples of 2-channel data is actually 40 raw data points.
|
||||
|
||||
if len(pcm)%enc.channels != 0 {
|
||||
return 0, fmt.Errorf("opus: input buffer length must be multiple of channels")
|
||||
return fmt.Errorf("opus: input buffer length must be multiple of channels")
|
||||
}
|
||||
samples := len(pcm) / enc.channels
|
||||
n := int(C.opus_encode(
|
||||
enc.p,
|
||||
(*C.opus_int16)(&pcm[0]),
|
||||
C.int(samples),
|
||||
(*C.uchar)(&data[0]),
|
||||
C.opus_int32(cap(data))))
|
||||
if n < 0 {
|
||||
return 0, Error(n)
|
||||
|
||||
n := int(C.ope_encoder_write(enc.p, (*C.opus_int16)(&pcm[0]), C.int(samples)))
|
||||
if n != 0 {
|
||||
return Error(n)
|
||||
}
|
||||
return n, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode raw PCM data and store the result in the supplied buffer. On success,
|
||||
// returns the number of bytes used up by the encoded data.
|
||||
func (enc *Encoder) EncodeFloat32(pcm []float32, data []byte) (int, error) {
|
||||
// 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 0, errEncUninitialized
|
||||
return errEncUninitialized
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return 0, fmt.Errorf("opus: no data supplied")
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, fmt.Errorf("opus: no target buffer")
|
||||
return fmt.Errorf("opus: no data supplied")
|
||||
}
|
||||
|
||||
if len(pcm)%enc.channels != 0 {
|
||||
return 0, fmt.Errorf("opus: input buffer length must be multiple of channels")
|
||||
return fmt.Errorf("opus: input buffer length must be multiple of channels")
|
||||
}
|
||||
samples := len(pcm) / enc.channels
|
||||
n := int(C.opus_encode_float(
|
||||
enc.p,
|
||||
(*C.float)(&pcm[0]),
|
||||
C.int(samples),
|
||||
(*C.uchar)(&data[0]),
|
||||
C.opus_int32(cap(data))))
|
||||
if n < 0 {
|
||||
return 0, Error(n)
|
||||
|
||||
n := int(C.ope_encoder_write_float(enc.p, (*C.float)(&pcm[0]), C.int(samples)))
|
||||
if n != 0 {
|
||||
return Error(n)
|
||||
}
|
||||
return n, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDTX configures the encoder's use of discontinuous transmission (DTX).
|
||||
|
|
|
@ -4,14 +4,17 @@
|
|||
|
||||
package opus
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncoderNew(t *testing.T) {
|
||||
enc, err := NewEncoder(48000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(48000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
enc, err = NewEncoder(12345, 1, AppVoIP)
|
||||
enc, err = NewEncoder(12345, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err == nil || enc != nil {
|
||||
t.Errorf("Expected error for illegal samplerate 12345")
|
||||
}
|
||||
|
@ -19,18 +22,18 @@ func TestEncoderNew(t *testing.T) {
|
|||
|
||||
func TestEncoderUnitialized(t *testing.T) {
|
||||
var enc Encoder
|
||||
_, err := enc.Encode(nil, nil)
|
||||
err := enc.Encode(nil)
|
||||
if err != errEncUninitialized {
|
||||
t.Errorf("Expected \"unitialized encoder\" error: %v", err)
|
||||
}
|
||||
_, err = enc.EncodeFloat32(nil, nil)
|
||||
err = enc.EncodeFloat32(nil)
|
||||
if err != errEncUninitialized {
|
||||
t.Errorf("Expected \"unitialized encoder\" error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncoderDTX(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
@ -51,9 +54,9 @@ func TestEncoderDTX(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoderSampleRate(t *testing.T) {
|
||||
sample_rates := []int{8000, 12000, 16000, 24000, 48000}
|
||||
for _, f := range sample_rates {
|
||||
enc, err := NewEncoder(f, 1, AppVoIP)
|
||||
sampleRates := []int{8000, 12000, 16000, 24000, 48000}
|
||||
for _, f := range sampleRates {
|
||||
enc, err := NewEncoder(f, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Fatalf("Error creating new encoder with sample_rate %d Hz: %v", f, err)
|
||||
}
|
||||
|
@ -68,7 +71,7 @@ func TestEncoderSampleRate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoder_SetGetBitrate(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
@ -78,6 +81,7 @@ func TestEncoder_SetGetBitrate(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Error("Error set bitrate:", err)
|
||||
}
|
||||
enc.FlushHeaders()
|
||||
br, err := enc.Bitrate()
|
||||
if err != nil {
|
||||
t.Error("Error getting bitrate", err)
|
||||
|
@ -89,7 +93,7 @@ func TestEncoder_SetGetBitrate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoder_SetBitrateToAuto(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
@ -125,7 +129,7 @@ func TestEncoder_SetBitrateToAuto(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoder_SetBitrateToMax(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
@ -161,7 +165,7 @@ func TestEncoder_SetBitrateToMax(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoder_SetGetInvalidBitrate(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
@ -183,7 +187,7 @@ func TestEncoder_SetGetInvalidBitrate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoder_SetGetComplexity(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
@ -205,7 +209,7 @@ func TestEncoder_SetGetComplexity(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoder_SetGetInvalidComplexity(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
@ -233,7 +237,7 @@ func TestEncoder_SetGetInvalidComplexity(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoder_SetGetMaxBandwidth(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
@ -261,7 +265,7 @@ func TestEncoder_SetGetMaxBandwidth(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoder_SetGetInBandFEC(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
@ -292,7 +296,7 @@ func TestEncoder_SetGetInBandFEC(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoder_SetGetPacketLossPerc(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
@ -314,7 +318,7 @@ func TestEncoder_SetGetPacketLossPerc(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncoder_SetGetInvalidPacketLossPerc(t *testing.T) {
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP)
|
||||
enc, err := NewEncoder(8000, 1, AppVoIP, new(bytes.Buffer))
|
||||
if err != nil || enc == nil {
|
||||
t.Errorf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
|
589
opus_test.go
589
opus_test.go
|
@ -5,6 +5,8 @@
|
|||
package opus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
@ -32,27 +34,38 @@ func TestCodec(t *testing.T) {
|
|||
const FRAME_SIZE_MS = 60
|
||||
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
|
||||
pcm := make([]int16, FRAME_SIZE)
|
||||
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
|
||||
|
||||
data := new(bytes.Buffer)
|
||||
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP, data)
|
||||
if err != nil || enc == nil {
|
||||
t.Fatalf("Error creating new encoder: %v", err)
|
||||
}
|
||||
addSine(pcm, SAMPLE_RATE, G4)
|
||||
data := make([]byte, 1000)
|
||||
n, err := enc.Encode(pcm, data)
|
||||
|
||||
err = enc.Encode(pcm)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't encode data: %v", err)
|
||||
}
|
||||
data = data[:n]
|
||||
dec, err := NewDecoder(SAMPLE_RATE, 1)
|
||||
enc.Close()
|
||||
|
||||
dec, err := NewStream(bytes.NewReader(data.Bytes()))
|
||||
if err != nil || dec == nil {
|
||||
t.Fatalf("Error creating new decoder: %v", err)
|
||||
}
|
||||
n, err = dec.Decode(data, pcm)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
|
||||
toRead := len(pcm)
|
||||
for toRead != 0 {
|
||||
n, err := dec.Read(pcm)
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
toRead -= n
|
||||
}
|
||||
if len(pcm) != n {
|
||||
t.Fatalf("Length mismatch: %d samples in, %d out", len(pcm), n)
|
||||
|
||||
if toRead != 0 {
|
||||
t.Fatalf("Length mismatch: %d samples in, %d out", len(pcm), len(pcm)-toRead)
|
||||
}
|
||||
// Checking the output programmatically is seriously not easy. I checked it
|
||||
// by hand and by ear, it looks fine. I'll just assume the API faithfully
|
||||
|
@ -66,23 +79,23 @@ func TestCodecFloat32(t *testing.T) {
|
|||
const FRAME_SIZE_MS = 60
|
||||
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
|
||||
pcm := make([]float32, FRAME_SIZE)
|
||||
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
|
||||
data := new(bytes.Buffer)
|
||||
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP, data)
|
||||
if err != nil || enc == nil {
|
||||
t.Fatalf("Error creating new encoder: %v", err)
|
||||
}
|
||||
addSineFloat32(pcm, SAMPLE_RATE, G4)
|
||||
data := make([]byte, 1000)
|
||||
n, err := enc.EncodeFloat32(pcm, data)
|
||||
err = enc.EncodeFloat32(pcm)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't encode data: %v", err)
|
||||
}
|
||||
dec, err := NewDecoder(SAMPLE_RATE, 1)
|
||||
dec, err := NewStream(bytes.NewReader(data.Bytes()))
|
||||
if err != nil || dec == nil {
|
||||
t.Fatalf("Error creating new decoder: %v", err)
|
||||
}
|
||||
// TODO: Uh-oh.. it looks like I forgot to put a data = data[:n] here, yet
|
||||
// the test is not failing. Why?
|
||||
n, err = dec.DecodeFloat32(data, pcm)
|
||||
n, err := dec.ReadFloat32(pcm)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
|
@ -90,549 +103,3 @@ func TestCodecFloat32(t *testing.T) {
|
|||
t.Fatalf("Length mismatch: %d samples in, %d out", len(pcm), n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodecFEC(t *testing.T) {
|
||||
// Create bogus input sound
|
||||
const G4 = 391.995
|
||||
const SAMPLE_RATE = 48000
|
||||
const FRAME_SIZE_MS = 10
|
||||
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
|
||||
const NUMBER_OF_FRAMES = 6
|
||||
pcm := make([]int16, 0)
|
||||
|
||||
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
|
||||
if err != nil || enc == nil {
|
||||
t.Fatalf("Error creating new encoder: %v", err)
|
||||
}
|
||||
enc.SetPacketLossPerc(30)
|
||||
enc.SetInBandFEC(true)
|
||||
|
||||
dec, err := NewDecoder(SAMPLE_RATE, 1)
|
||||
if err != nil || dec == nil {
|
||||
t.Fatalf("Error creating new decoder: %v", err)
|
||||
}
|
||||
|
||||
mono := make([]int16, FRAME_SIZE*NUMBER_OF_FRAMES)
|
||||
addSine(mono, SAMPLE_RATE, G4)
|
||||
|
||||
encodedData := make([][]byte, NUMBER_OF_FRAMES)
|
||||
for i, idx := 0, 0; i < len(mono); i += FRAME_SIZE {
|
||||
data := make([]byte, 1000)
|
||||
n, err := enc.Encode(mono[i:i+FRAME_SIZE], data)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't encode data: %v", err)
|
||||
}
|
||||
data = data[:n]
|
||||
encodedData[idx] = data
|
||||
idx++
|
||||
}
|
||||
|
||||
lost := false
|
||||
for i := 0; i < len(encodedData); i++ {
|
||||
// "Dropping" packets 2 and 4
|
||||
if i == 2 || i == 4 {
|
||||
lost = true
|
||||
continue
|
||||
}
|
||||
if lost {
|
||||
samples, err := dec.LastPacketDuration()
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't get last packet duration: %v", err)
|
||||
}
|
||||
|
||||
pcmBuffer := make([]int16, samples)
|
||||
if err = dec.DecodeFEC(encodedData[i], pcmBuffer); err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
pcm = append(pcm, pcmBuffer...)
|
||||
lost = false
|
||||
}
|
||||
|
||||
pcmBuffer := make([]int16, NUMBER_OF_FRAMES*FRAME_SIZE)
|
||||
n, err := dec.Decode(encodedData[i], pcmBuffer)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
pcmBuffer = pcmBuffer[:n]
|
||||
if n != FRAME_SIZE {
|
||||
t.Fatalf("Length mismatch: %d samples expected, %d out", FRAME_SIZE, n)
|
||||
}
|
||||
pcm = append(pcm, pcmBuffer...)
|
||||
}
|
||||
|
||||
if len(mono) != len(pcm) {
|
||||
t.Fatalf("Input/Output length mismatch: %d samples in, %d out", len(mono), len(pcm))
|
||||
}
|
||||
|
||||
// Commented out for the same reason as in TestStereo
|
||||
/*
|
||||
fmt.Printf("in,out\n")
|
||||
for i := range mono {
|
||||
fmt.Printf("%d,%d\n", mono[i], pcm[i])
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
func TestCodecFECFloat32(t *testing.T) {
|
||||
// Create bogus input sound
|
||||
const G4 = 391.995
|
||||
const SAMPLE_RATE = 48000
|
||||
const FRAME_SIZE_MS = 10
|
||||
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
|
||||
const NUMBER_OF_FRAMES = 6
|
||||
pcm := make([]float32, 0)
|
||||
|
||||
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
|
||||
if err != nil || enc == nil {
|
||||
t.Fatalf("Error creating new encoder: %v", err)
|
||||
}
|
||||
enc.SetPacketLossPerc(30)
|
||||
enc.SetInBandFEC(true)
|
||||
|
||||
dec, err := NewDecoder(SAMPLE_RATE, 1)
|
||||
if err != nil || dec == nil {
|
||||
t.Fatalf("Error creating new decoder: %v", err)
|
||||
}
|
||||
|
||||
mono := make([]float32, FRAME_SIZE*NUMBER_OF_FRAMES)
|
||||
addSineFloat32(mono, SAMPLE_RATE, G4)
|
||||
|
||||
encodedData := make([][]byte, NUMBER_OF_FRAMES)
|
||||
for i, idx := 0, 0; i < len(mono); i += FRAME_SIZE {
|
||||
data := make([]byte, 1000)
|
||||
n, err := enc.EncodeFloat32(mono[i:i+FRAME_SIZE], data)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't encode data: %v", err)
|
||||
}
|
||||
data = data[:n]
|
||||
encodedData[idx] = data
|
||||
idx++
|
||||
}
|
||||
|
||||
lost := false
|
||||
for i := 0; i < len(encodedData); i++ {
|
||||
// "Dropping" packets 2 and 4
|
||||
if i == 2 || i == 4 {
|
||||
lost = true
|
||||
continue
|
||||
}
|
||||
if lost {
|
||||
samples, err := dec.LastPacketDuration()
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't get last packet duration: %v", err)
|
||||
}
|
||||
|
||||
pcmBuffer := make([]float32, samples)
|
||||
if err = dec.DecodeFECFloat32(encodedData[i], pcmBuffer); err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
pcm = append(pcm, pcmBuffer...)
|
||||
lost = false
|
||||
}
|
||||
|
||||
pcmBuffer := make([]float32, NUMBER_OF_FRAMES*FRAME_SIZE)
|
||||
n, err := dec.DecodeFloat32(encodedData[i], pcmBuffer)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
pcmBuffer = pcmBuffer[:n]
|
||||
if n != FRAME_SIZE {
|
||||
t.Fatalf("Length mismatch: %d samples expected, %d out", FRAME_SIZE, n)
|
||||
}
|
||||
pcm = append(pcm, pcmBuffer...)
|
||||
}
|
||||
|
||||
if len(mono) != len(pcm) {
|
||||
t.Fatalf("Input/Output length mismatch: %d samples in, %d out", len(mono), len(pcm))
|
||||
}
|
||||
|
||||
// Commented out for the same reason as in TestStereo
|
||||
/*
|
||||
fmt.Printf("in,out\n")
|
||||
for i := 0; i < len(mono); i++ {
|
||||
fmt.Printf("%f,%f\n", mono[i], pcm[i])
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
func TestCodecPLC(t *testing.T) {
|
||||
// Create bogus input sound
|
||||
const G4 = 391.995
|
||||
const SAMPLE_RATE = 48000
|
||||
const FRAME_SIZE_MS = 10
|
||||
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
|
||||
const NUMBER_OF_FRAMES = 6
|
||||
pcm := make([]int16, 0)
|
||||
|
||||
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
|
||||
if err != nil || enc == nil {
|
||||
t.Fatalf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
||||
dec, err := NewDecoder(SAMPLE_RATE, 1)
|
||||
if err != nil || dec == nil {
|
||||
t.Fatalf("Error creating new decoder: %v", err)
|
||||
}
|
||||
|
||||
mono := make([]int16, FRAME_SIZE*NUMBER_OF_FRAMES)
|
||||
addSine(mono, SAMPLE_RATE, G4)
|
||||
|
||||
encodedData := make([][]byte, NUMBER_OF_FRAMES)
|
||||
for i, idx := 0, 0; i < len(mono); i += FRAME_SIZE {
|
||||
data := make([]byte, 1000)
|
||||
n, err := enc.Encode(mono[i:i+FRAME_SIZE], data)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't encode data: %v", err)
|
||||
}
|
||||
data = data[:n]
|
||||
encodedData[idx] = data
|
||||
idx++
|
||||
}
|
||||
|
||||
lost := false
|
||||
for i := 0; i < len(encodedData); i++ {
|
||||
// "Dropping" packets 2 and 4
|
||||
if i == 2 || i == 4 {
|
||||
lost = true
|
||||
continue
|
||||
}
|
||||
if lost {
|
||||
samples, err := dec.LastPacketDuration()
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't get last packet duration: %v", err)
|
||||
}
|
||||
|
||||
pcmBuffer := make([]int16, samples)
|
||||
if err = dec.DecodePLC(pcmBuffer); err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
nonZero := 0
|
||||
for n := range pcmBuffer {
|
||||
if pcmBuffer[n] != 0 {
|
||||
nonZero++
|
||||
}
|
||||
}
|
||||
if nonZero == 0 {
|
||||
t.Fatalf("DecodePLC produced only zero samples")
|
||||
}
|
||||
pcm = append(pcm, pcmBuffer...)
|
||||
lost = false
|
||||
}
|
||||
|
||||
pcmBuffer := make([]int16, NUMBER_OF_FRAMES*FRAME_SIZE)
|
||||
n, err := dec.Decode(encodedData[i], pcmBuffer)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
pcmBuffer = pcmBuffer[:n]
|
||||
if n != FRAME_SIZE {
|
||||
t.Fatalf("Length mismatch: %d samples expected, %d out", FRAME_SIZE, n)
|
||||
}
|
||||
pcm = append(pcm, pcmBuffer...)
|
||||
}
|
||||
|
||||
if len(mono) != len(pcm) {
|
||||
t.Fatalf("Input/Output length mismatch: %d samples in, %d out", len(mono), len(pcm))
|
||||
}
|
||||
|
||||
// Commented out for the same reason as in TestStereo
|
||||
/*
|
||||
fmt.Printf("in,out\n")
|
||||
for i := range mono {
|
||||
fmt.Printf("%d,%d\n", mono[i], pcm[i])
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
func TestCodecPLCFloat32(t *testing.T) {
|
||||
// Create bogus input sound
|
||||
const G4 = 391.995
|
||||
const SAMPLE_RATE = 48000
|
||||
const FRAME_SIZE_MS = 10
|
||||
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
|
||||
const NUMBER_OF_FRAMES = 6
|
||||
pcm := make([]float32, 0)
|
||||
|
||||
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
|
||||
if err != nil || enc == nil {
|
||||
t.Fatalf("Error creating new encoder: %v", err)
|
||||
}
|
||||
|
||||
dec, err := NewDecoder(SAMPLE_RATE, 1)
|
||||
if err != nil || dec == nil {
|
||||
t.Fatalf("Error creating new decoder: %v", err)
|
||||
}
|
||||
|
||||
mono := make([]float32, FRAME_SIZE*NUMBER_OF_FRAMES)
|
||||
addSineFloat32(mono, SAMPLE_RATE, G4)
|
||||
|
||||
encodedData := make([][]byte, NUMBER_OF_FRAMES)
|
||||
for i, idx := 0, 0; i < len(mono); i += FRAME_SIZE {
|
||||
data := make([]byte, 1000)
|
||||
n, err := enc.EncodeFloat32(mono[i:i+FRAME_SIZE], data)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't encode data: %v", err)
|
||||
}
|
||||
data = data[:n]
|
||||
encodedData[idx] = data
|
||||
idx++
|
||||
}
|
||||
|
||||
lost := false
|
||||
for i := 0; i < len(encodedData); i++ {
|
||||
// "Dropping" packets 2 and 4
|
||||
if i == 2 || i == 4 {
|
||||
lost = true
|
||||
continue
|
||||
}
|
||||
if lost {
|
||||
samples, err := dec.LastPacketDuration()
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't get last packet duration: %v", err)
|
||||
}
|
||||
|
||||
pcmBuffer := make([]float32, samples)
|
||||
if err = dec.DecodePLCFloat32(pcmBuffer); err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
nonZero := 0
|
||||
for n := range pcmBuffer {
|
||||
if pcmBuffer[n] != 0.0 {
|
||||
nonZero++
|
||||
}
|
||||
}
|
||||
if nonZero == 0 {
|
||||
t.Fatalf("DecodePLC produced only zero samples")
|
||||
}
|
||||
pcm = append(pcm, pcmBuffer...)
|
||||
lost = false
|
||||
}
|
||||
|
||||
pcmBuffer := make([]float32, NUMBER_OF_FRAMES*FRAME_SIZE)
|
||||
n, err := dec.DecodeFloat32(encodedData[i], pcmBuffer)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
pcmBuffer = pcmBuffer[:n]
|
||||
if n != FRAME_SIZE {
|
||||
t.Fatalf("Length mismatch: %d samples expected, %d out", FRAME_SIZE, n)
|
||||
}
|
||||
pcm = append(pcm, pcmBuffer...)
|
||||
}
|
||||
|
||||
if len(mono) != len(pcm) {
|
||||
t.Fatalf("Input/Output length mismatch: %d samples in, %d out", len(mono), len(pcm))
|
||||
}
|
||||
|
||||
// Commented out for the same reason as in TestStereo
|
||||
/*
|
||||
fmt.Printf("in,out\n")
|
||||
for i := 0; i < len(mono); i++ {
|
||||
fmt.Printf("%f,%f\n", mono[i], pcm[i])
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
func TestStereo(t *testing.T) {
|
||||
// Create bogus input sound
|
||||
const G4 = 391.995
|
||||
const E3 = 164.814
|
||||
const SAMPLE_RATE = 48000
|
||||
const FRAME_SIZE_MS = 60
|
||||
const CHANNELS = 2
|
||||
const FRAME_SIZE_MONO = SAMPLE_RATE * FRAME_SIZE_MS / 1000
|
||||
|
||||
enc, err := NewEncoder(SAMPLE_RATE, CHANNELS, AppVoIP)
|
||||
if err != nil || enc == nil {
|
||||
t.Fatalf("Error creating new encoder: %v", err)
|
||||
}
|
||||
dec, err := NewDecoder(SAMPLE_RATE, CHANNELS)
|
||||
if err != nil || dec == nil {
|
||||
t.Fatalf("Error creating new decoder: %v", err)
|
||||
}
|
||||
|
||||
// Source signal (left G4, right E3)
|
||||
left := make([]int16, FRAME_SIZE_MONO)
|
||||
right := make([]int16, FRAME_SIZE_MONO)
|
||||
addSine(left, SAMPLE_RATE, G4)
|
||||
addSine(right, SAMPLE_RATE, E3)
|
||||
pcm := interleave(left, right)
|
||||
|
||||
data := make([]byte, 1000)
|
||||
n, err := enc.Encode(pcm, data)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't encode data: %v", err)
|
||||
}
|
||||
data = data[:n]
|
||||
n, err = dec.Decode(data, pcm)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
if n*CHANNELS != len(pcm) {
|
||||
t.Fatalf("Length mismatch: %d samples in, %d out", len(pcm), n*CHANNELS)
|
||||
}
|
||||
|
||||
// This is hard to check programatically, but I plotted the graphs in a
|
||||
// spreadsheet and it looked great. The encoded waves both start out with a
|
||||
// string of zero samples before they pick up, and the G4 is phase shifted
|
||||
// by half a period, but that's all fine for lossy audio encoding.
|
||||
/*
|
||||
leftdec, rightdec := split(pcm)
|
||||
fmt.Printf("left_in,left_out,right_in,right_out\n")
|
||||
for i := range left {
|
||||
fmt.Printf("%d,%d,%d,%d\n", left[i], leftdec[i], right[i], rightdec[i])
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
func TestBufferSize(t *testing.T) {
|
||||
const G4 = 391.995
|
||||
const SAMPLE_RATE = 48000
|
||||
const FRAME_SIZE_MS = 60
|
||||
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000
|
||||
const GUARD_SIZE = 100
|
||||
|
||||
checkGuardInt16 := func(t *testing.T, s []int16) {
|
||||
for n := range s {
|
||||
if s[n] != 0 {
|
||||
t.Fatal("Memory corruption detected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkGuardFloat32 := func(t *testing.T, s []float32) {
|
||||
for n := range s {
|
||||
if s[n] != 0 {
|
||||
t.Fatal("Memory corruption detected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkResult := func(t *testing.T, n int, err error, expectOK bool) {
|
||||
if expectOK {
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't decode data: %v", err)
|
||||
}
|
||||
if n != FRAME_SIZE {
|
||||
t.Fatalf("Length mismatch: %d samples in, %d out", FRAME_SIZE, n)
|
||||
}
|
||||
} else {
|
||||
if err == nil {
|
||||
t.Fatalf("Expected decode failure, but it succeeded")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
encodeFrame := func(t *testing.T) []byte {
|
||||
pcm := make([]int16, FRAME_SIZE)
|
||||
enc, err := NewEncoder(SAMPLE_RATE, 1, AppVoIP)
|
||||
if err != nil || enc == nil {
|
||||
t.Fatalf("Error creating new encoder: %v", err)
|
||||
}
|
||||
addSine(pcm, SAMPLE_RATE, G4)
|
||||
data := make([]byte, 1000)
|
||||
n, err := enc.Encode(pcm, data)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't encode data: %v", err)
|
||||
}
|
||||
return data[:n]
|
||||
}
|
||||
|
||||
createDecoder := func(t *testing.T) *Decoder {
|
||||
dec, err := NewDecoder(SAMPLE_RATE, 1)
|
||||
if err != nil || dec == nil {
|
||||
t.Fatalf("Error creating new decoder: %v", err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
decodeInt16 := func(t *testing.T, data []byte, decodeSize int, expectOK bool) {
|
||||
dec := createDecoder(t)
|
||||
decodedMem := make([]int16, decodeSize+GUARD_SIZE*2)
|
||||
decodedRef := decodedMem[GUARD_SIZE : GUARD_SIZE+decodeSize : GUARD_SIZE+decodeSize]
|
||||
n, err := dec.Decode(data, decodedRef)
|
||||
checkGuardInt16(t, decodedMem[:GUARD_SIZE])
|
||||
checkGuardInt16(t, decodedMem[decodeSize+GUARD_SIZE:])
|
||||
checkResult(t, n, err, expectOK)
|
||||
}
|
||||
|
||||
decodeFloat32 := func(t *testing.T, data []byte, decodeSize int, expectOK bool) {
|
||||
dec := createDecoder(t)
|
||||
decodedMem := make([]float32, decodeSize+GUARD_SIZE*2)
|
||||
decodedRef := decodedMem[GUARD_SIZE : GUARD_SIZE+decodeSize : GUARD_SIZE+decodeSize]
|
||||
n, err := dec.DecodeFloat32(data, decodedRef)
|
||||
checkGuardFloat32(t, decodedMem[:GUARD_SIZE])
|
||||
checkGuardFloat32(t, decodedMem[decodeSize+GUARD_SIZE:])
|
||||
checkResult(t, n, err, expectOK)
|
||||
}
|
||||
|
||||
decodeFecInt16 := func(t *testing.T, data []byte, decodeSize int, expectOK bool) {
|
||||
dec := createDecoder(t)
|
||||
decodedMem := make([]int16, decodeSize+GUARD_SIZE*2)
|
||||
decodedRef := decodedMem[GUARD_SIZE : GUARD_SIZE+decodeSize : GUARD_SIZE+decodeSize]
|
||||
err := dec.DecodeFEC(data, decodedRef)
|
||||
checkGuardInt16(t, decodedMem[:GUARD_SIZE])
|
||||
checkGuardInt16(t, decodedMem[decodeSize+GUARD_SIZE:])
|
||||
checkResult(t, FRAME_SIZE, err, expectOK)
|
||||
}
|
||||
|
||||
decodeFecFloat32 := func(t *testing.T, data []byte, decodeSize int, expectOK bool) {
|
||||
dec := createDecoder(t)
|
||||
decodedMem := make([]float32, decodeSize+GUARD_SIZE*2)
|
||||
decodedRef := decodedMem[GUARD_SIZE : GUARD_SIZE+decodeSize : GUARD_SIZE+decodeSize]
|
||||
err := dec.DecodeFECFloat32(data, decodedRef)
|
||||
checkGuardFloat32(t, decodedMem[:GUARD_SIZE])
|
||||
checkGuardFloat32(t, decodedMem[decodeSize+GUARD_SIZE:])
|
||||
checkResult(t, FRAME_SIZE, err, expectOK)
|
||||
}
|
||||
|
||||
t.Run("smaller-buffer-int16", func(t *testing.T) {
|
||||
decodeInt16(t, encodeFrame(t), FRAME_SIZE-1, false)
|
||||
})
|
||||
|
||||
t.Run("smaller-buffer-float32", func(t *testing.T) {
|
||||
decodeFloat32(t, encodeFrame(t), FRAME_SIZE-1, false)
|
||||
})
|
||||
|
||||
t.Run("smaller-buffer-int16-fec", func(t *testing.T) {
|
||||
decodeFecFloat32(t, encodeFrame(t), FRAME_SIZE-1, false)
|
||||
})
|
||||
|
||||
t.Run("smaller-buffer-float32-fec", func(t *testing.T) {
|
||||
decodeFecFloat32(t, encodeFrame(t), FRAME_SIZE-1, false)
|
||||
})
|
||||
|
||||
t.Run("exact-buffer-int16", func(t *testing.T) {
|
||||
decodeInt16(t, encodeFrame(t), FRAME_SIZE, true)
|
||||
})
|
||||
|
||||
t.Run("exact-buffer-float32", func(t *testing.T) {
|
||||
decodeFloat32(t, encodeFrame(t), FRAME_SIZE, true)
|
||||
})
|
||||
|
||||
t.Run("exact-buffer-int16-fec", func(t *testing.T) {
|
||||
decodeFecInt16(t, encodeFrame(t), FRAME_SIZE, true)
|
||||
})
|
||||
|
||||
t.Run("exact-buffer-float32-fec", func(t *testing.T) {
|
||||
decodeFecFloat32(t, encodeFrame(t), FRAME_SIZE, true)
|
||||
})
|
||||
|
||||
t.Run("larger-buffer-int16", func(t *testing.T) {
|
||||
decodeInt16(t, encodeFrame(t), FRAME_SIZE+1, true)
|
||||
})
|
||||
|
||||
t.Run("larger-buffer-float32", func(t *testing.T) {
|
||||
decodeFloat32(t, encodeFrame(t), FRAME_SIZE+1, true)
|
||||
})
|
||||
|
||||
t.Run("larger-buffer-int16-fec", func(t *testing.T) {
|
||||
decodeFecInt16(t, encodeFrame(t), FRAME_SIZE+1, false)
|
||||
})
|
||||
|
||||
t.Run("larger-buffer-float32-fec", func(t *testing.T) {
|
||||
decodeFecFloat32(t, encodeFrame(t), FRAME_SIZE+1, false)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue