mirror of https://bitbucket.org/ausocean/av.git
Merged in unexport-chunksize (pull request #281)
Unexport chunksize from ALSA device Approved-by: kortschak <dan@kortschak.io> Approved-by: Alan Noble <anoble@gmail.com>
This commit is contained in:
commit
aff04e6ffa
|
@ -151,9 +151,9 @@ func main() {
|
||||||
Data: ab.Data,
|
Data: ab.Data,
|
||||||
}
|
}
|
||||||
|
|
||||||
recSize := (((len(ac.pb.Data) / ac.dev.BufferFormat().Channels) * ac.channels) / ac.dev.BufferFormat().Rate) * ac.rate
|
cs := pcm.DataSize(ac.parameters.rate, ac.parameters.channels, ac.parameters.bits, float64(ac.parameters.period), 0)
|
||||||
rbLen := rbDuration / ac.period
|
rbLen := rbDuration / ac.period
|
||||||
ac.rb = ring.NewBuffer(rbLen, recSize, rbTimeout)
|
ac.rb = ring.NewBuffer(rbLen, cs, rbTimeout)
|
||||||
|
|
||||||
go ac.input()
|
go ac.input()
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,15 @@ import (
|
||||||
|
|
||||||
// ByteLexer is used to lex bytes using a buffer size which is configured upon construction.
|
// ByteLexer is used to lex bytes using a buffer size which is configured upon construction.
|
||||||
type ByteLexer struct {
|
type ByteLexer struct {
|
||||||
bufSize *int
|
bufSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewByteLexer returns a pointer to a ByteLexer with the given buffer size.
|
// NewByteLexer returns a pointer to a ByteLexer with the given buffer size.
|
||||||
func NewByteLexer(bufSize *int) *ByteLexer {
|
func NewByteLexer(s int) (*ByteLexer, error) {
|
||||||
return &ByteLexer{bufSize: bufSize}
|
if s <= 0 {
|
||||||
|
return nil, fmt.Errorf("invalid buffer size: %v", s)
|
||||||
|
}
|
||||||
|
return &ByteLexer{bufSize: s}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// zeroTicks can be used to create an instant ticker.
|
// zeroTicks can be used to create an instant ticker.
|
||||||
|
@ -48,15 +51,8 @@ func init() {
|
||||||
close(zeroTicks)
|
close(zeroTicks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lex reads *l.bufSize bytes from src and writes them to dst every d seconds.
|
// Lex reads l.bufSize bytes from src and writes them to dst every d seconds.
|
||||||
func (l *ByteLexer) Lex(dst io.Writer, src io.Reader, d time.Duration) error {
|
func (l *ByteLexer) Lex(dst io.Writer, src io.Reader, d time.Duration) error {
|
||||||
if l.bufSize == nil {
|
|
||||||
return fmt.Errorf("buffer size has not been set")
|
|
||||||
}
|
|
||||||
bufSize := *l.bufSize
|
|
||||||
if bufSize <= 0 {
|
|
||||||
return fmt.Errorf("invalid buffer size: %v", bufSize)
|
|
||||||
}
|
|
||||||
if d < 0 {
|
if d < 0 {
|
||||||
return fmt.Errorf("invalid delay: %v", d)
|
return fmt.Errorf("invalid delay: %v", d)
|
||||||
}
|
}
|
||||||
|
@ -69,7 +65,7 @@ func (l *ByteLexer) Lex(dst io.Writer, src io.Reader, d time.Duration) error {
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, bufSize)
|
buf := make([]byte, l.bufSize)
|
||||||
for {
|
for {
|
||||||
<-ticker.C
|
<-ticker.C
|
||||||
off, err := src.Read(buf)
|
off, err := src.Read(buf)
|
||||||
|
|
|
@ -51,8 +51,15 @@ func TestByteLexer(t *testing.T) {
|
||||||
for i, tt := range lexTests {
|
for i, tt := range lexTests {
|
||||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
dst := bytes.NewBuffer([]byte{})
|
dst := bytes.NewBuffer([]byte{})
|
||||||
l := NewByteLexer(&tt.n)
|
l, err := NewByteLexer(tt.n)
|
||||||
err := l.Lex(dst, bytes.NewReader(tt.data), tt.t)
|
if err != nil {
|
||||||
|
if tt.isValid {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = l.Lex(dst, bytes.NewReader(tt.data), tt.t)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
if tt.isValid {
|
if tt.isValid {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
|
|
@ -32,6 +32,8 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"bitbucket.org/ausocean/av/codec/adpcm"
|
||||||
|
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,6 +67,15 @@ type Buffer struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DataSize takes audio attributes describing audio data and returns the size of that data.
|
||||||
|
func DataSize(rate, channels, bitDepth int, period float64, codec uint8) int {
|
||||||
|
s := int(float64(channels) * float64(rate) * float64(bitDepth/8) * period)
|
||||||
|
if codec == codecutil.ADPCM {
|
||||||
|
s = adpcm.EncBytes(s)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// Resample takes Buffer c and resamples the pcm audio data to 'rate' Hz and returns a Buffer with the resampled data.
|
// Resample takes Buffer c and resamples the pcm audio data to 'rate' Hz and returns a Buffer with the resampled data.
|
||||||
// Notes:
|
// Notes:
|
||||||
// - Currently only downsampling is implemented and c's rate must be divisible by 'rate' or an error will occur.
|
// - Currently only downsampling is implemented and c's rate must be divisible by 'rate' or an error will occur.
|
||||||
|
|
|
@ -47,7 +47,7 @@ import (
|
||||||
const (
|
const (
|
||||||
pkg = "alsa: "
|
pkg = "alsa: "
|
||||||
rbTimeout = 100 * time.Millisecond
|
rbTimeout = 100 * time.Millisecond
|
||||||
rbNextTimeout = 100 * time.Millisecond
|
rbNextTimeout = 2000 * time.Millisecond
|
||||||
rbLen = 200
|
rbLen = 200
|
||||||
defaultSampleRate = 48000
|
defaultSampleRate = 48000
|
||||||
)
|
)
|
||||||
|
@ -70,7 +70,6 @@ type ALSA struct {
|
||||||
dev *yalsa.Device // ALSA device's Audio input device.
|
dev *yalsa.Device // ALSA device's Audio input device.
|
||||||
pb pcm.Buffer // Buffer to contain the direct audio from ALSA.
|
pb pcm.Buffer // Buffer to contain the direct audio from ALSA.
|
||||||
rb *ring.Buffer // Ring buffer to contain processed audio ready to be read.
|
rb *ring.Buffer // Ring buffer to contain processed audio ready to be read.
|
||||||
chunkSize int // This is the number of bytes that will be stored in rb at a time.
|
|
||||||
Config // Configuration parameters for this device.
|
Config // Configuration parameters for this device.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +92,7 @@ type Logger interface {
|
||||||
// OpenError is used to determine whether an error has originated from attempting to open a device.
|
// OpenError is used to determine whether an error has originated from attempting to open a device.
|
||||||
type OpenError error
|
type OpenError error
|
||||||
|
|
||||||
// NewALSA initializes and returns an ALSA device which has its logger set as the given logger.
|
// New initializes and returns an ALSA device which has its logger set as the given logger.
|
||||||
func New(l Logger) *ALSA { return &ALSA{l: l} }
|
func New(l Logger) *ALSA { return &ALSA{l: l} }
|
||||||
|
|
||||||
// Set will take a Config struct, check the validity of the relevant fields
|
// Set will take a Config struct, check the validity of the relevant fields
|
||||||
|
@ -149,24 +148,9 @@ func (d *ALSA) Set(c config.Config) error {
|
||||||
Data: ab.Data,
|
Data: ab.Data,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account for channel conversion.
|
|
||||||
chunkSize := float64(len(d.pb.Data) / d.dev.BufferFormat().Channels * d.Channels)
|
|
||||||
|
|
||||||
// Account for resampling.
|
|
||||||
chunkSize = (chunkSize / float64(d.dev.BufferFormat().Rate)) * float64(d.SampleRate)
|
|
||||||
if chunkSize < 1 {
|
|
||||||
return errors.New("given Config parameters are too small")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Account for codec conversion.
|
|
||||||
if d.Codec == codecutil.ADPCM {
|
|
||||||
d.chunkSize = adpcm.EncBytes(int(chunkSize))
|
|
||||||
} else {
|
|
||||||
d.chunkSize = int(chunkSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create ring buffer with appropriate chunk size.
|
// Create ring buffer with appropriate chunk size.
|
||||||
d.rb = ring.NewBuffer(rbLen, d.chunkSize, rbTimeout)
|
cs := pcm.DataSize(d.SampleRate, d.Channels, d.BitDepth, d.RecPeriod, 0)
|
||||||
|
d.rb = ring.NewBuffer(rbLen, cs, rbTimeout)
|
||||||
|
|
||||||
// Start device in paused mode.
|
// Start device in paused mode.
|
||||||
d.mode = paused
|
d.mode = paused
|
||||||
|
@ -206,31 +190,6 @@ func (d *ALSA) Stop() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChunkSize returns the number of bytes written to the ringbuffer per d.RecPeriod.
|
|
||||||
func (d *ALSA) ChunkSize() int {
|
|
||||||
return d.chunkSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate checks if Config parameters are valid and returns an error if they are not.
|
|
||||||
func validate(c *Config) error {
|
|
||||||
if c.SampleRate <= 0 {
|
|
||||||
return fmt.Errorf("invalid sample rate: %v", c.SampleRate)
|
|
||||||
}
|
|
||||||
if c.Channels <= 0 {
|
|
||||||
return fmt.Errorf("invalid number of channels: %v", c.Channels)
|
|
||||||
}
|
|
||||||
if c.BitDepth <= 0 {
|
|
||||||
return fmt.Errorf("invalid bitdepth: %v", c.BitDepth)
|
|
||||||
}
|
|
||||||
if c.RecPeriod <= 0 {
|
|
||||||
return fmt.Errorf("invalid recording period: %v", c.RecPeriod)
|
|
||||||
}
|
|
||||||
if !codecutil.IsValid(c.Codec) {
|
|
||||||
return errors.New("invalid codec")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the recording device with the given name and prepare it to record.
|
// open the recording device with the given name and prepare it to record.
|
||||||
// If name is empty, the first recording device is used.
|
// If name is empty, the first recording device is used.
|
||||||
func (d *ALSA) open() error {
|
func (d *ALSA) open() error {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/codec/codecutil"
|
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||||
|
"bitbucket.org/ausocean/av/codec/pcm"
|
||||||
"bitbucket.org/ausocean/av/revid/config"
|
"bitbucket.org/ausocean/av/revid/config"
|
||||||
"bitbucket.org/ausocean/utils/logger"
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
)
|
)
|
||||||
|
@ -63,8 +64,8 @@ func TestDevice(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
chunkSize := ai.ChunkSize()
|
cs := pcm.DataSize(c.SampleRate, c.Channels, c.BitDepth, c.RecPeriod, c.InputCodec)
|
||||||
lexer := codecutil.NewByteLexer(&chunkSize)
|
lexer, err := codecutil.NewByteLexer(cs)
|
||||||
go lexer.Lex(ioutil.Discard, ai, time.Duration(c.RecPeriod*float64(time.Second)))
|
go lexer.Lex(ioutil.Discard, ai, time.Duration(c.RecPeriod*float64(time.Second)))
|
||||||
time.Sleep(time.Duration(c.RecPeriod*float64(time.Second)) * time.Duration(n))
|
time.Sleep(time.Duration(c.RecPeriod*float64(time.Second)) * time.Duration(n))
|
||||||
ai.Stop()
|
ai.Stop()
|
||||||
|
|
|
@ -237,7 +237,6 @@ type Config struct {
|
||||||
RecPeriod float64 // How many seconds to record at a time.
|
RecPeriod float64 // How many seconds to record at a time.
|
||||||
Channels int // Number of audio channels, 1 for mono, 2 for stereo.
|
Channels int // Number of audio channels, 1 for mono, 2 for stereo.
|
||||||
BitDepth int // Sample bit depth.
|
BitDepth int // Sample bit depth.
|
||||||
ChunkSize int // ChunkSize is the size of the chunks in the audio.Device's ringbuffer.
|
|
||||||
|
|
||||||
RTPAddress string // RTPAddress defines the RTP output destination.
|
RTPAddress string // RTPAddress defines the RTP output destination.
|
||||||
BurstPeriod uint // BurstPeriod defines the revid burst period in seconds.
|
BurstPeriod uint // BurstPeriod defines the revid burst period in seconds.
|
||||||
|
|
|
@ -42,6 +42,7 @@ import (
|
||||||
"bitbucket.org/ausocean/av/codec/h264"
|
"bitbucket.org/ausocean/av/codec/h264"
|
||||||
"bitbucket.org/ausocean/av/codec/h265"
|
"bitbucket.org/ausocean/av/codec/h265"
|
||||||
"bitbucket.org/ausocean/av/codec/mjpeg"
|
"bitbucket.org/ausocean/av/codec/mjpeg"
|
||||||
|
"bitbucket.org/ausocean/av/codec/pcm"
|
||||||
"bitbucket.org/ausocean/av/container/flv"
|
"bitbucket.org/ausocean/av/container/flv"
|
||||||
"bitbucket.org/ausocean/av/container/mts"
|
"bitbucket.org/ausocean/av/container/mts"
|
||||||
"bitbucket.org/ausocean/av/device"
|
"bitbucket.org/ausocean/av/device"
|
||||||
|
@ -363,8 +364,13 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
|
||||||
}
|
}
|
||||||
|
|
||||||
r.input = alsa.New(r.cfg.Logger)
|
r.input = alsa.New(r.cfg.Logger)
|
||||||
cs := r.input.(*alsa.ALSA).ChunkSize()
|
|
||||||
r.lexTo = codecutil.NewByteLexer(&cs).Lex
|
l, err := codecutil.NewByteLexer(pcm.DataSize(r.cfg.SampleRate, r.cfg.Channels, r.cfg.BitDepth, r.cfg.RecPeriod, r.cfg.InputCodec))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.lexTo = l.Lex
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the input device. We know that defaults are set, so no need to
|
// Configure the input device. We know that defaults are set, so no need to
|
||||||
|
|
Loading…
Reference in New Issue