mirror of https://bitbucket.org/ausocean/av.git
Merged in channel-nego (pull request #424)
alsa: negotiate requested num of channels, fix error logging and checking Approved-by: Saxon Milton
This commit is contained in:
commit
755b9a6687
|
@ -142,6 +142,9 @@ func (d *ALSA) Setup(c config.Config) error {
|
|||
errs = append(errs, errInvalidCodec)
|
||||
c.InputCodec = defaultCodec
|
||||
}
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
d.Config = Config{
|
||||
SampleRate: c.SampleRate,
|
||||
Channels: c.Channels,
|
||||
|
@ -153,16 +156,14 @@ func (d *ALSA) Setup(c config.Config) error {
|
|||
// Open the requested audio device.
|
||||
err := d.open()
|
||||
if err != nil {
|
||||
d.l.Log(logger.Error, pkg+"failed to open device")
|
||||
return err
|
||||
return fmt.Errorf("failed to open device: %w", err)
|
||||
}
|
||||
|
||||
// Setup the device to record with desired period.
|
||||
ab := d.dev.NewBufferDuration(time.Duration(d.RecPeriod * float64(time.Second)))
|
||||
sf, err := pcm.SFFromString(ab.Format.SampleFormat.String())
|
||||
if err != nil {
|
||||
d.l.Log(logger.Error, pkg+err.Error())
|
||||
return err
|
||||
return fmt.Errorf("unable to get sample format from string: %w", err)
|
||||
}
|
||||
cf := pcm.BufferFormat{
|
||||
SFormat: sf,
|
||||
|
@ -229,20 +230,20 @@ func (d *ALSA) Stop() error {
|
|||
func (d *ALSA) open() error {
|
||||
// Close any existing device.
|
||||
if d.dev != nil {
|
||||
d.l.Log(logger.Debug, pkg+"closing device", "title", d.title)
|
||||
d.l.Log(logger.Debug, "closing device", "title", d.title)
|
||||
d.dev.Close()
|
||||
d.dev = nil
|
||||
}
|
||||
|
||||
// Open sound card and open recording device.
|
||||
d.l.Log(logger.Debug, pkg+"opening sound card")
|
||||
d.l.Log(logger.Debug, "opening sound card")
|
||||
cards, err := yalsa.OpenCards()
|
||||
if err != nil {
|
||||
return OpenError(err)
|
||||
}
|
||||
defer yalsa.CloseCards(cards)
|
||||
|
||||
d.l.Log(logger.Debug, pkg+"finding audio device")
|
||||
d.l.Log(logger.Debug, "finding audio device")
|
||||
for _, card := range cards {
|
||||
devices, err := card.Devices()
|
||||
if err != nil {
|
||||
|
@ -262,19 +263,22 @@ func (d *ALSA) open() error {
|
|||
return OpenError(errors.New("no ALSA device found"))
|
||||
}
|
||||
|
||||
d.l.Log(logger.Debug, pkg+"opening ALSA device", "title", d.dev.Title)
|
||||
d.l.Log(logger.Debug, "opening ALSA device", "title", d.dev.Title)
|
||||
err = d.dev.Open()
|
||||
if err != nil {
|
||||
return OpenError(err)
|
||||
}
|
||||
|
||||
// 2 channels is what most devices need to record in. If mono is requested,
|
||||
// the recording will be converted in formatBuffer().
|
||||
channels, err := d.dev.NegotiateChannels(2)
|
||||
if err != nil {
|
||||
return OpenError(err)
|
||||
// Try to configure device with chosen channels.
|
||||
channels, err := d.dev.NegotiateChannels(int(d.Channels))
|
||||
if err != nil && d.Channels == 1 {
|
||||
d.l.Log(logger.Info, "device is unable to record in mono, trying stereo", "error", err)
|
||||
channels, err = d.dev.NegotiateChannels(2)
|
||||
}
|
||||
d.l.Log(logger.Debug, pkg+"alsa device channels set", "channels", channels)
|
||||
if err != nil {
|
||||
return OpenError(fmt.Errorf("device is unable to record with requested number of channels: %w", err))
|
||||
}
|
||||
d.l.Log(logger.Debug, "alsa device channels set", "channels", channels)
|
||||
|
||||
// Try to negotiate a rate to record in that is divisible by the wanted rate
|
||||
// so that it can be easily downsampled to the wanted rate.
|
||||
|
@ -294,7 +298,7 @@ func (d *ALSA) open() error {
|
|||
rate, err = d.dev.NegotiateRate(r)
|
||||
if err == nil {
|
||||
foundRate = true
|
||||
d.l.Log(logger.Debug, pkg+"alsa device sample rate set", "rate", rate)
|
||||
d.l.Log(logger.Debug, "alsa device sample rate set", "rate", rate)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -302,12 +306,12 @@ func (d *ALSA) open() error {
|
|||
|
||||
// If no easily divisible rate is found, then use the default rate.
|
||||
if !foundRate {
|
||||
d.l.Log(logger.Warning, pkg+"Unable to sample at requested rate, default used.", "rateRequested", d.SampleRate)
|
||||
d.l.Log(logger.Warning, "unable to sample at requested rate, default used.", "rateRequested", d.SampleRate)
|
||||
rate, err = d.dev.NegotiateRate(defaultSampleRate)
|
||||
if err != nil {
|
||||
return OpenError(err)
|
||||
}
|
||||
d.l.Log(logger.Debug, pkg+"alsa device sample rate set", "rate", rate)
|
||||
d.l.Log(logger.Debug, "alsa device sample rate set", "rate", rate)
|
||||
}
|
||||
|
||||
var aFmt yalsa.FormatType
|
||||
|
@ -332,7 +336,7 @@ func (d *ALSA) open() error {
|
|||
default:
|
||||
return OpenError(fmt.Errorf("unsupported sample bits %v", d.BitDepth))
|
||||
}
|
||||
d.l.Log(logger.Debug, pkg+"alsa device bit depth set", "bitdepth", bitdepth)
|
||||
d.l.Log(logger.Debug, "alsa device bit depth set", "bitdepth", bitdepth)
|
||||
|
||||
// A 50ms period is a sensible value for low-ish latency. (this could be made configurable if needed)
|
||||
// Some devices only accept even period sizes while others want powers of 2.
|
||||
|
@ -347,13 +351,13 @@ func (d *ALSA) open() error {
|
|||
if err != nil {
|
||||
return OpenError(err)
|
||||
}
|
||||
d.l.Log(logger.Debug, pkg+"alsa device buffer size set", "buffersize", bufSize)
|
||||
d.l.Log(logger.Debug, "alsa device buffer size set", "buffersize", bufSize)
|
||||
|
||||
if err = d.dev.Prepare(); err != nil {
|
||||
return OpenError(err)
|
||||
}
|
||||
|
||||
d.l.Log(logger.Debug, pkg+"successfully negotiated device params")
|
||||
d.l.Log(logger.Debug, "successfully negotiated device params")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -371,7 +375,7 @@ func (d *ALSA) input() {
|
|||
continue
|
||||
case stopped:
|
||||
if d.dev != nil {
|
||||
d.l.Log(logger.Debug, pkg+"closing ALSA device", "title", d.title)
|
||||
d.l.Log(logger.Debug, "closing ALSA device", "title", d.title)
|
||||
d.dev.Close()
|
||||
d.dev = nil
|
||||
}
|
||||
|
@ -379,31 +383,31 @@ func (d *ALSA) input() {
|
|||
}
|
||||
|
||||
// Read from audio device.
|
||||
d.l.Log(logger.Debug, pkg+"recording audio for period", "seconds", d.RecPeriod)
|
||||
d.l.Log(logger.Debug, "recording audio for period", "seconds", d.RecPeriod)
|
||||
err := d.dev.Read(d.pb.Data)
|
||||
if err != nil {
|
||||
d.l.Log(logger.Debug, pkg+"read failed", "error", err.Error())
|
||||
d.l.Log(logger.Debug, "read failed", "error", err.Error())
|
||||
err = d.open() // re-open
|
||||
if err != nil {
|
||||
d.l.Log(logger.Fatal, pkg+"reopening device failed", "error", err.Error())
|
||||
d.l.Log(logger.Fatal, "reopening device failed", "error", err.Error())
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Process audio.
|
||||
d.l.Log(logger.Debug, pkg+"processing audio")
|
||||
d.l.Log(logger.Debug, "processing audio")
|
||||
toWrite := d.formatBuffer()
|
||||
|
||||
// Write audio to ringbuffer.
|
||||
n, err := d.rb.Write(toWrite.Data)
|
||||
switch err {
|
||||
case nil:
|
||||
d.l.Log(logger.Debug, pkg+"wrote audio to ringbuffer", "length", n)
|
||||
d.l.Log(logger.Debug, "wrote audio to ringbuffer", "length", n)
|
||||
case ring.ErrDropped:
|
||||
d.l.Log(logger.Warning, pkg+"old audio data overwritten")
|
||||
d.l.Log(logger.Warning, "old audio data overwritten")
|
||||
default:
|
||||
d.l.Log(logger.Error, pkg+"unexpected ringbuffer error", "error", err.Error())
|
||||
d.l.Log(logger.Error, "unexpected ringbuffer error", "error", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -436,7 +440,7 @@ func (d *ALSA) formatBuffer() pcm.Buffer {
|
|||
if d.pb.Format.Channels == 2 && d.Channels == 1 {
|
||||
formatted, err = pcm.StereoToMono(d.pb)
|
||||
if err != nil {
|
||||
d.l.Log(logger.Fatal, pkg+"channel conversion failed", "error", err.Error())
|
||||
d.l.Log(logger.Fatal, "channel conversion failed", "error", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -445,7 +449,7 @@ func (d *ALSA) formatBuffer() pcm.Buffer {
|
|||
// Convert rate.
|
||||
formatted, err = pcm.Resample(formatted, d.SampleRate)
|
||||
if err != nil {
|
||||
d.l.Log(logger.Fatal, pkg+"rate conversion failed", "error", err.Error())
|
||||
d.l.Log(logger.Fatal, "rate conversion failed", "error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,11 +460,11 @@ func (d *ALSA) formatBuffer() pcm.Buffer {
|
|||
enc := adpcm.NewEncoder(b)
|
||||
_, err = enc.Write(formatted.Data)
|
||||
if err != nil {
|
||||
d.l.Log(logger.Fatal, pkg+"unable to encode", "error", err.Error())
|
||||
d.l.Log(logger.Fatal, "unable to encode", "error", err.Error())
|
||||
}
|
||||
formatted.Data = b.Bytes()
|
||||
default:
|
||||
d.l.Log(logger.Error, pkg+"unhandled audio codec")
|
||||
d.l.Log(logger.Error, "unhandled audio codec")
|
||||
}
|
||||
|
||||
return formatted
|
||||
|
|
|
@ -27,6 +27,7 @@ package alsa
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
|
@ -35,6 +36,7 @@ import (
|
|||
|
||||
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||
"bitbucket.org/ausocean/av/codec/pcm"
|
||||
"bitbucket.org/ausocean/av/device"
|
||||
"bitbucket.org/ausocean/av/revid/config"
|
||||
"bitbucket.org/ausocean/utils/logger"
|
||||
)
|
||||
|
@ -54,14 +56,14 @@ func TestDevice(t *testing.T) {
|
|||
l := logger.New(logger.Debug, os.Stderr, true)
|
||||
ai := New(l)
|
||||
err := ai.Setup(c)
|
||||
// If there was an error opening the device, skip this test.
|
||||
if _, ok := err.(OpenError); ok {
|
||||
// Log any config errors, otherwise if there was an error opening a device, skip
|
||||
// this test since not all testing environments will have recording devices.
|
||||
var e *device.MultiError
|
||||
if err != nil && errors.As(err, &e) {
|
||||
t.Logf("errors from configuring device: %s", err.Error())
|
||||
} else if err != nil {
|
||||
t.Skip(err)
|
||||
}
|
||||
// For any other error, report it.
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = ai.Start()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module bitbucket.org/ausocean/av
|
|||
go 1.13
|
||||
|
||||
require (
|
||||
bitbucket.org/ausocean/iot v1.2.15
|
||||
bitbucket.org/ausocean/iot v1.2.16
|
||||
bitbucket.org/ausocean/utils v1.2.14
|
||||
github.com/Comcast/gots v0.0.0-20190305015453-8d56e473f0f7
|
||||
github.com/go-audio/audio v0.0.0-20181013203223-7b2a6ca21480
|
||||
|
|
5
go.sum
5
go.sum
|
@ -2,8 +2,8 @@ bitbucket.org/ausocean/iot v1.2.13 h1:E9LcW3HYqRgJqxNhPJUCfVRvoV2IAU4B7JSDNxB/x2
|
|||
bitbucket.org/ausocean/iot v1.2.13/go.mod h1:Q5FwaOKnCty3dVeVtki6DLwYa5vhNpOaeu1lwLyPCg8=
|
||||
bitbucket.org/ausocean/iot v1.2.14 h1:+2up1G1g28RnXFw+1tpobdpZxzZDYEXgsXwmpNZDCWA=
|
||||
bitbucket.org/ausocean/iot v1.2.14/go.mod h1:Q5FwaOKnCty3dVeVtki6DLwYa5vhNpOaeu1lwLyPCg8=
|
||||
bitbucket.org/ausocean/iot v1.2.15 h1:fJ3hP/jZ/5irLn74bMIlztKrKAqpdhbNR/QiNz5ZgPE=
|
||||
bitbucket.org/ausocean/iot v1.2.15/go.mod h1:rRcWt6SoM/jgIZpP1zrpnKb5BhxIMulAJ+q1xTvLh94=
|
||||
bitbucket.org/ausocean/iot v1.2.16 h1:dZvENB2cLmeHvEfBULhGxnM9K10IuWFLqjcFKBJHpgw=
|
||||
bitbucket.org/ausocean/iot v1.2.16/go.mod h1:rRcWt6SoM/jgIZpP1zrpnKb5BhxIMulAJ+q1xTvLh94=
|
||||
bitbucket.org/ausocean/utils v1.2.11 h1:zA0FOaPjN960ryp8PKCkV5y50uWBYrIxCVnXjwbvPqg=
|
||||
bitbucket.org/ausocean/utils v1.2.11/go.mod h1:uXzX9z3PLemyURTMWRhVI8uLhPX4uuvaaO85v2hcob8=
|
||||
bitbucket.org/ausocean/utils v1.2.13 h1:tUaIywtoMc1+zl1GCVQokX4mL5X7LNHX5O51AgAPrWA=
|
||||
|
@ -31,7 +31,6 @@ github.com/go-audio/audio v0.0.0-20181013203223-7b2a6ca21480 h1:4sGU+UABMMsRJyD+
|
|||
github.com/go-audio/audio v0.0.0-20181013203223-7b2a6ca21480/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
|
||||
github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884 h1:2TaXIaVA4ff/MHHezOj83tCypALTFAcXOImcFWNa3jw=
|
||||
github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884/go.mod h1:UiqzUyfX0zs3pJ/DPyvS5v8sN6s5bXPUDDIVA5v8dks=
|
||||
github.com/go-delve/delve v1.5.0 h1:gQsRvFdR0BGk19NROQZsAv6iG4w5QIZoJlxJeEUBb0c=
|
||||
github.com/go-delve/delve v1.5.0/go.mod h1:c6b3a1Gry6x8a4LGCe/CWzrocrfaHvkUxCj3k4bvSUQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
|
|
|
@ -25,11 +25,13 @@ LICENSE
|
|||
package revid
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||
"bitbucket.org/ausocean/av/container/mts"
|
||||
"bitbucket.org/ausocean/av/device"
|
||||
"bitbucket.org/ausocean/av/device/alsa"
|
||||
"bitbucket.org/ausocean/utils/logger"
|
||||
)
|
||||
|
@ -42,8 +44,11 @@ func (r *Revid) setupAudio() error {
|
|||
// Configure ALSA device.
|
||||
r.cfg.Logger.Log(logger.Debug, "configuring input device")
|
||||
err := d.Setup(r.cfg)
|
||||
if err != nil {
|
||||
var e *device.MultiError
|
||||
if err != nil && errors.As(err, &e) {
|
||||
r.cfg.Logger.Log(logger.Warning, "errors from configuring input device", "errors", err)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
r.cfg.Logger.Log(logger.Info, "input device configured")
|
||||
|
||||
|
|
Loading…
Reference in New Issue