diff --git a/cmd/revid-cli/main.go b/cmd/revid-cli/main.go index ab55188a..b0fc89a1 100644 --- a/cmd/revid-cli/main.go +++ b/cmd/revid-cli/main.go @@ -138,7 +138,7 @@ func handleFlags() revid.Config { // Audio specific flags. sampleRatePtr = flag.Int("SampleRate", 48000, "Sample rate of recorded audio") channelsPtr = flag.Int("Channels", 1, "Record in Mono or Stereo (1 or 2)") - recPeriodPtr = flag.Int("recPeriod", 1, "How many seconds to record at a time") + recPeriodPtr = flag.Float64("recPeriod", 1, "How many seconds to record at a time") bitDepthPtr = flag.Int("bitDepth", 16, "Bit Depth to record audio at.") ) @@ -209,9 +209,9 @@ func handleFlags() revid.Config { switch *inputPtr { case "Audio": - cfg.WriteRate = uint(*recPeriodPtr) + cfg.WriteRate = 1.0 / (*recPeriodPtr) default: - cfg.WriteRate = *frameRatePtr + cfg.WriteRate = float64(*frameRatePtr) } if len(outputs) == 0 { diff --git a/revid/audio-input.go b/revid/audio-input.go index 40e80abb..c19a4d21 100644 --- a/revid/audio-input.go +++ b/revid/audio-input.go @@ -52,6 +52,8 @@ const ( stopped ) +var rates = [8]int{8000, 16000, 32000, 44100, 48000, 88200, 96000, 192000} + var log *logger.Logger // audioDevice holds everything we need to know about the audio input stream. @@ -141,7 +143,7 @@ func (a *audioDevice) Start() { a.mu.Unlock() } -// Stop will stop recording audio and close +// Stop will stop recording audio and close the device func (a *audioDevice) Stop() { a.mu.Lock() a.mode = stopped @@ -178,7 +180,7 @@ func (a *audioDevice) open() error { for _, card := range cards { devices, err := card.Devices() if err != nil { - return err + continue } for _, dev := range devices { if dev.Type != alsa.PCM || !dev.Record { @@ -214,8 +216,7 @@ func (a *audioDevice) open() error { // so that it can be easily downsampled to the wanted rate. // Note: if a card thinks it can record at a rate but can't actually, this can cause a failure. Eg. // the audioinjector is supposed to record at 8000Hz and 16000Hz but it can't due to a firmware issue, - // to fix this 8000 and 16000 must be removed from this slice. - rates := [8]int{8000, 16000, 32000, 44100, 48000, 88200, 96000, 192000} + // to fix this 8000 and 16000 must be removed from the rates slice. foundRate := false for i := 0; i < len(rates) && !foundRate; i++ { if rates[i] < a.SampleRate { diff --git a/revid/audio-input_test.go b/revid/audio-input_test.go index a77158a6..0a1cb44e 100644 --- a/revid/audio-input_test.go +++ b/revid/audio-input_test.go @@ -3,32 +3,80 @@ package revid import ( "testing" - "bitbucket.org/ausocean/av/container/mts" - "bitbucket.org/ausocean/av/container/mts/meta" - "bitbucket.org/ausocean/iot/pi/netsender" + "github.com/yobert/alsa" ) -func TestAudioPipeline(t *testing.T) { - mts.Meta = meta.New() - var logger testLogger - ns, err := netsender.New(&logger, nil, nil, nil) - if err != nil { - t.Errorf("netsender.New failed with error %v", err) +func TestAudioDevice(t *testing.T) { + // We want to open a device with a standard configuration. + ac := &AudioConfig{ + SampleRate: 8000, + Channels: 1, + RecPeriod: 1, + BitDepth: 16, + Codec: ADPCM, } - var c Config - c.Logger = &logger - c.Input = File - c.InputPath = "../../test/test-data/av/input/original_8kHz_adpcm_test.pcm" - c.Outputs = []uint8{File} - c.OutputPath = "./test-temp" - - rv, err := New(c, ns) + // Check that a device exists with the desired parameters. + cards, err := alsa.OpenCards() if err != nil { - t.Errorf("revid.New failed with error %v", err) - } else if rv == nil { - t.Errorf("revid.New did not return a new revid") + t.Skip("skipping, no audio card found") } + defer alsa.CloseCards(cards) + var testDev *alsa.Device + for _, card := range cards { + devices, err := card.Devices() + if err != nil { + continue + } + for _, dev := range devices { + if dev.Type != alsa.PCM || !dev.Record { + continue + } + testDev = dev + break + } + } + if testDev == nil { + t.Skip("skipping, no suitable audio device found") + } + _, err = testDev.NegotiateChannels(2) + if err != nil { + t.Skip("skipping, no suitable audio device found") + } + foundRate := false + for i := 0; i < len(rates) && !foundRate; i++ { + if rates[i] < ac.SampleRate { + continue + } + if rates[i]%ac.SampleRate == 0 { + _, err = testDev.NegotiateRate(rates[i]) + if err == nil { + foundRate = true + } + } + } + if !foundRate { + _, err = testDev.NegotiateRate(defaultSampleRate) + if err != nil { + t.Skip("skipping, no suitable audio device found") + } + } + _, err = testDev.NegotiateFormat(alsa.S16_LE) + if err != nil { + t.Skip("skipping, no suitable audio device found") + } + _, err = testDev.NegotiateBufferSize(8192, 16384) + if err != nil { + t.Skip("skipping, no suitable audio device found") + } + if err = testDev.Prepare(); err != nil { + t.Skip("skipping, no suitable audio device found") + } + testDev.Close() - rv.Stop() + ai := NewAudioDevice(ac) + + ai.Start() + + ai.Stop() } diff --git a/revid/config.go b/revid/config.go index d914e221..82a940a1 100644 --- a/revid/config.go +++ b/revid/config.go @@ -53,7 +53,7 @@ type Config struct { IntraRefreshPeriod uint RtpAddress string SendRetry bool - WriteRate uint // How many times a second revid encoders will be written to. + WriteRate float64 // How many times a second revid encoders will be written to. // Video Height uint diff --git a/revid/revid.go b/revid/revid.go index 9fa2f560..982910d7 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -161,8 +161,8 @@ func (r *Revid) reset(config Config) error { r.config.Logger.SetLevel(config.LogLevel) err = r.setupPipeline( - func(dst io.WriteCloser, fps, medType int) (io.WriteCloser, error) { - e := mts.NewEncoder(dst, float64(fps), medType) + func(dst io.WriteCloser, fps float64, medType int) (io.WriteCloser, error) { + e := mts.NewEncoder(dst, fps, medType) return e, nil }, func(dst io.WriteCloser, fps int) (io.WriteCloser, error) { @@ -196,7 +196,7 @@ func (r *Revid) setConfig(config Config) error { // mtsEnc and flvEnc will be called to obtain an mts encoder and flv encoder // respectively. multiWriter will be used to create an ioext.multiWriteCloser // so that encoders can write to multiple senders. -func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate, mediaType int) (io.WriteCloser, error), flvEnc func(dst io.WriteCloser, rate int) (io.WriteCloser, error), multiWriter func(...io.WriteCloser) io.WriteCloser) error { +func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64, mediaType int) (io.WriteCloser, error), flvEnc func(dst io.WriteCloser, rate int) (io.WriteCloser, error), multiWriter func(...io.WriteCloser) io.WriteCloser) error { // encoders will hold the encoders that are required for revid's current // configuration. var encoders []io.WriteCloser @@ -246,7 +246,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate, mediaType in } else { mediaType = mts.Video } - e, _ := mtsEnc(mw, int(r.config.WriteRate), mediaType) + e, _ := mtsEnc(mw, r.config.WriteRate, mediaType) encoders = append(encoders, e) } diff --git a/revid/revid_test.go b/revid/revid_test.go index 17739221..ece494ca 100644 --- a/revid/revid_test.go +++ b/revid/revid_test.go @@ -232,7 +232,7 @@ func TestResetEncoderSenderSetup(t *testing.T) { // This logic is what we want to check. err = rv.setupPipeline( - func(dst io.WriteCloser, rate int, mediaType int) (io.WriteCloser, error) { + func(dst io.WriteCloser, rate float64, mediaType int) (io.WriteCloser, error) { return &tstMtsEncoder{dst: dst}, nil }, func(dst io.WriteCloser, rate int) (io.WriteCloser, error) {