mirror of https://bitbucket.org/ausocean/av.git
revid and audio: seperated audio into own package
audio device input is now handle in its own package which resides in the new input directory a list of codecs was added to codecutil package to help with multiple packages using the same codecs
This commit is contained in:
parent
3e2ff49420
commit
96c1b51173
|
@ -38,6 +38,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||||
"bitbucket.org/ausocean/av/container/mts"
|
"bitbucket.org/ausocean/av/container/mts"
|
||||||
"bitbucket.org/ausocean/av/container/mts/meta"
|
"bitbucket.org/ausocean/av/container/mts/meta"
|
||||||
"bitbucket.org/ausocean/av/revid"
|
"bitbucket.org/ausocean/av/revid"
|
||||||
|
@ -200,11 +201,11 @@ func handleFlags() revid.Config {
|
||||||
|
|
||||||
switch *inputCodecPtr {
|
switch *inputCodecPtr {
|
||||||
case "H264":
|
case "H264":
|
||||||
cfg.InputCodec = revid.H264
|
cfg.InputCodec = codecutil.H264
|
||||||
case "PCM":
|
case "PCM":
|
||||||
cfg.InputCodec = revid.PCM
|
cfg.InputCodec = codecutil.PCM
|
||||||
case "ADPCM":
|
case "ADPCM":
|
||||||
cfg.InputCodec = revid.ADPCM
|
cfg.InputCodec = codecutil.ADPCM
|
||||||
case "":
|
case "":
|
||||||
default:
|
default:
|
||||||
log.Log(logger.Error, pkg+"bad input codec argument")
|
log.Log(logger.Error, pkg+"bad input codec argument")
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
NAME
|
||||||
|
list.go
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Trek Hopton <trek@ausocean.org>
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
This file is Copyright (C) 2019 the Australian Ocean Lab (AusOcean)
|
||||||
|
|
||||||
|
It is free software: you can redistribute it and/or modify them
|
||||||
|
under the terms of the GNU General Public License as published by the
|
||||||
|
Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License in gpl.txt.
|
||||||
|
If not, see [GNU licenses](http://www.gnu.org/licenses).
|
||||||
|
*/
|
||||||
|
|
||||||
|
package codecutil
|
||||||
|
|
||||||
|
// A global list containing all available codecs for reference in any application.
|
||||||
|
const (
|
||||||
|
PCM = iota
|
||||||
|
ADPCM
|
||||||
|
H264
|
||||||
|
H265
|
||||||
|
MJPEG
|
||||||
|
)
|
|
@ -1,12 +1,13 @@
|
||||||
/*
|
/*
|
||||||
NAME
|
NAME
|
||||||
audio-input.go
|
audio.go
|
||||||
|
|
||||||
AUTHOR
|
AUTHOR
|
||||||
|
Alan Noble <alan@ausocean.org>
|
||||||
Trek Hopton <trek@ausocean.org>
|
Trek Hopton <trek@ausocean.org>
|
||||||
|
|
||||||
LICENSE
|
LICENSE
|
||||||
audio-input.go is Copyright (C) 2019 the Australian Ocean Lab (AusOcean)
|
This file is Copyright (C) 2019 the Australian Ocean Lab (AusOcean)
|
||||||
|
|
||||||
It is free software: you can redistribute it and/or modify them
|
It is free software: you can redistribute it and/or modify them
|
||||||
under the terms of the GNU General Public License as published by the
|
under the terms of the GNU General Public License as published by the
|
||||||
|
@ -22,7 +23,8 @@ LICENSE
|
||||||
If not, see [GNU licenses](http://www.gnu.org/licenses).
|
If not, see [GNU licenses](http://www.gnu.org/licenses).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package revid
|
// Package audio provides access to input from audio devices.
|
||||||
|
package audio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -35,15 +37,18 @@ import (
|
||||||
"github.com/yobert/alsa"
|
"github.com/yobert/alsa"
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/codec/adpcm"
|
"bitbucket.org/ausocean/av/codec/adpcm"
|
||||||
|
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||||
"bitbucket.org/ausocean/av/codec/pcm"
|
"bitbucket.org/ausocean/av/codec/pcm"
|
||||||
"bitbucket.org/ausocean/utils/logger"
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
"bitbucket.org/ausocean/utils/ring"
|
"bitbucket.org/ausocean/utils/ring"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
rbTimeout = 100 * time.Millisecond
|
pkg = "pkg: "
|
||||||
rbNextTimeout = 100 * time.Millisecond
|
rbTimeout = 100 * time.Millisecond
|
||||||
rbLen = 200
|
rbNextTimeout = 100 * time.Millisecond
|
||||||
|
rbLen = 200
|
||||||
|
defaultSampleRate = 48000
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -52,30 +57,31 @@ const (
|
||||||
stopped
|
stopped
|
||||||
)
|
)
|
||||||
|
|
||||||
// Rates contains the audio sample rates used by revid.
|
// Rates contains the audio sample rates used by audio.
|
||||||
var Rates = [8]int{8000, 16000, 32000, 44100, 48000, 88200, 96000, 192000}
|
var Rates = [8]int{8000, 16000, 32000, 44100, 48000, 88200, 96000, 192000}
|
||||||
|
|
||||||
// AudioDevice holds everything we need to know about the audio input stream.
|
// Device holds everything we need to know about the audio input stream.
|
||||||
type AudioDevice struct {
|
type Device struct {
|
||||||
l Logger
|
l Logger
|
||||||
mu sync.Mutex
|
|
||||||
source string // Name of audio source, or empty for the default source.
|
|
||||||
// Operating mode, either running, paused, or stopped.
|
// Operating mode, either running, paused, or stopped.
|
||||||
// "running" means the input goroutine is reading from the ALSA device and writing to the ringbuffer.
|
// "running" means the input goroutine is reading from the ALSA device and writing to the ringbuffer.
|
||||||
// "paused" means the input routine is sleeping until unpaused or stopped.
|
// "paused" means the input routine is sleeping until unpaused or stopped.
|
||||||
// "stopped" means the input routine is stopped and the ALSA device is closed.
|
// "stopped" means the input routine is stopped and the ALSA device is closed.
|
||||||
mode uint8
|
mode uint8
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
title string // Name of audio title, or empty for the default title.
|
||||||
dev *alsa.Device // Audio input device.
|
dev *alsa.Device // Audio input device.
|
||||||
ab alsa.Buffer // ALSA's buffer.
|
ab alsa.Buffer // ALSA's buffer.
|
||||||
rb *ring.Buffer // Our buffer.
|
rb *ring.Buffer // Our buffer.
|
||||||
chunkSize int // This is the number of bytes that will be stored at a time.
|
chunkSize int // This is the number of bytes that will be stored at a time.
|
||||||
|
|
||||||
*AudioConfig
|
*Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// AudioConfig provides parameters used by AudioDevice.
|
// Config provides parameters used by Device.
|
||||||
type AudioConfig struct {
|
type Config struct {
|
||||||
SampleRate int
|
SampleRate int
|
||||||
Channels int
|
Channels int
|
||||||
BitDepth int
|
BitDepth int
|
||||||
|
@ -83,10 +89,17 @@ type AudioConfig struct {
|
||||||
Codec uint8
|
Codec uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAudioDevice initializes and returns an AudioDevice which can be started, read from, and stopped.
|
// Logger enables any implementation of a logger to be used.
|
||||||
func NewAudioDevice(cfg *AudioConfig, l Logger) (*AudioDevice, error) {
|
// TODO: Make this part of the logger package.
|
||||||
a := &AudioDevice{}
|
type Logger interface {
|
||||||
a.AudioConfig = cfg
|
SetLevel(int8)
|
||||||
|
Log(level int8, message string, params ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDevice initializes and returns an Device which can be started, read from, and stopped.
|
||||||
|
func NewDevice(cfg *Config, l Logger) (*Device, error) {
|
||||||
|
a := &Device{}
|
||||||
|
a.Config = cfg
|
||||||
a.l = l
|
a.l = l
|
||||||
|
|
||||||
// Open the requested audio device.
|
// Open the requested audio device.
|
||||||
|
@ -100,10 +113,10 @@ func NewAudioDevice(cfg *AudioConfig, l Logger) (*AudioDevice, error) {
|
||||||
a.ab = a.dev.NewBufferDuration(time.Duration(a.RecPeriod * float64(time.Second)))
|
a.ab = a.dev.NewBufferDuration(time.Duration(a.RecPeriod * float64(time.Second)))
|
||||||
cs := (float64((len(a.ab.Data)/a.dev.BufferFormat().Channels)*a.Channels) / float64(a.dev.BufferFormat().Rate)) * float64(a.SampleRate)
|
cs := (float64((len(a.ab.Data)/a.dev.BufferFormat().Channels)*a.Channels) / float64(a.dev.BufferFormat().Rate)) * float64(a.SampleRate)
|
||||||
if cs < 1 {
|
if cs < 1 {
|
||||||
a.l.Log(logger.Error, pkg+"given AudioConfig parameters are too small", "error", err.Error())
|
a.l.Log(logger.Error, pkg+"given Config parameters are too small", "error", err.Error())
|
||||||
return nil, errors.New("given AudioConfig parameters are too small")
|
return nil, errors.New("given Config parameters are too small")
|
||||||
}
|
}
|
||||||
if a.Codec == ADPCM {
|
if a.Codec == codecutil.ADPCM {
|
||||||
a.chunkSize = adpcm.EncBytes(int(cs))
|
a.chunkSize = adpcm.EncBytes(int(cs))
|
||||||
} else {
|
} else {
|
||||||
a.chunkSize = int(cs)
|
a.chunkSize = int(cs)
|
||||||
|
@ -117,7 +130,7 @@ func NewAudioDevice(cfg *AudioConfig, l Logger) (*AudioDevice, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start will start recording audio and writing to the ringbuffer.
|
// Start will start recording audio and writing to the ringbuffer.
|
||||||
func (a *AudioDevice) Start() error {
|
func (a *Device) Start() error {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
mode := a.mode
|
mode := a.mode
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
|
@ -138,23 +151,23 @@ func (a *AudioDevice) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop will stop recording audio and close the device.
|
// Stop will stop recording audio and close the device.
|
||||||
func (a *AudioDevice) Stop() {
|
func (a *Device) Stop() {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
a.mode = stopped
|
a.mode = stopped
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChunkSize returns the number of bytes written to the ringbuffer per a.RecPeriod.
|
// ChunkSize returns the number of bytes written to the ringbuffer per a.RecPeriod.
|
||||||
func (a *AudioDevice) ChunkSize() int {
|
func (a *Device) ChunkSize() int {
|
||||||
return a.chunkSize
|
return a.chunkSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (a *AudioDevice) open() error {
|
func (a *Device) open() error {
|
||||||
// Close any existing device.
|
// Close any existing device.
|
||||||
if a.dev != nil {
|
if a.dev != nil {
|
||||||
a.l.Log(logger.Debug, pkg+"closing device", "source", a.source)
|
a.l.Log(logger.Debug, pkg+"closing device", "title", a.title)
|
||||||
a.dev.Close()
|
a.dev.Close()
|
||||||
a.dev = nil
|
a.dev = nil
|
||||||
}
|
}
|
||||||
|
@ -178,7 +191,7 @@ func (a *AudioDevice) open() error {
|
||||||
if dev.Type != alsa.PCM || !dev.Record {
|
if dev.Type != alsa.PCM || !dev.Record {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if dev.Title == a.source || a.source == "" {
|
if dev.Title == a.title || a.title == "" {
|
||||||
a.dev = dev
|
a.dev = dev
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -189,7 +202,7 @@ func (a *AudioDevice) open() error {
|
||||||
return errors.New("no audio device found")
|
return errors.New("no audio device found")
|
||||||
}
|
}
|
||||||
|
|
||||||
a.l.Log(logger.Debug, pkg+"opening audio device", "source", a.dev.Title)
|
a.l.Log(logger.Debug, pkg+"opening audio device", "title", a.dev.Title)
|
||||||
err = a.dev.Open()
|
err = a.dev.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.l.Log(logger.Debug, pkg+"failed to open audio device")
|
a.l.Log(logger.Debug, pkg+"failed to open audio device")
|
||||||
|
@ -261,7 +274,7 @@ func (a *AudioDevice) open() error {
|
||||||
|
|
||||||
// input continously records audio and writes it to the ringbuffer.
|
// input continously records audio and writes it to the ringbuffer.
|
||||||
// Re-opens the device and tries again if ASLA returns an error.
|
// Re-opens the device and tries again if ASLA returns an error.
|
||||||
func (a *AudioDevice) input() {
|
func (a *Device) input() {
|
||||||
for {
|
for {
|
||||||
// Check mode.
|
// Check mode.
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
|
@ -273,7 +286,7 @@ func (a *AudioDevice) input() {
|
||||||
continue
|
continue
|
||||||
case stopped:
|
case stopped:
|
||||||
if a.dev != nil {
|
if a.dev != nil {
|
||||||
a.l.Log(logger.Debug, pkg+"closing audio device", "source", a.source)
|
a.l.Log(logger.Debug, pkg+"closing audio device", "title", a.title)
|
||||||
a.dev.Close()
|
a.dev.Close()
|
||||||
a.dev = nil
|
a.dev = nil
|
||||||
}
|
}
|
||||||
|
@ -311,9 +324,8 @@ func (a *AudioDevice) input() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read reads a full PCM chunk from the ringbuffer, returning the number of bytes read upon success.
|
// Read reads from the ringbuffer, returning the number of bytes read upon success.
|
||||||
// Any errors returned are unexpected and should be considered fatal.
|
func (a *Device) Read(p []byte) (n int, err error) {
|
||||||
func (a *AudioDevice) Read(p []byte) (n int, err error) {
|
|
||||||
// Ready ringbuffer for read.
|
// Ready ringbuffer for read.
|
||||||
_, err = a.rb.Next(rbNextTimeout)
|
_, err = a.rb.Next(rbNextTimeout)
|
||||||
switch err {
|
switch err {
|
||||||
|
@ -338,7 +350,7 @@ func (a *AudioDevice) Read(p []byte) (n int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatBuffer returns audio that has been converted to the desired format.
|
// formatBuffer returns audio that has been converted to the desired format.
|
||||||
func (a *AudioDevice) formatBuffer() alsa.Buffer {
|
func (a *Device) formatBuffer() alsa.Buffer {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// If nothing needs to be changed, return the original.
|
// If nothing needs to be changed, return the original.
|
||||||
|
@ -367,8 +379,8 @@ func (a *AudioDevice) formatBuffer() alsa.Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch a.Codec {
|
switch a.Codec {
|
||||||
case PCM:
|
case codecutil.PCM:
|
||||||
case ADPCM:
|
case codecutil.ADPCM:
|
||||||
b := bytes.NewBuffer(make([]byte, 0, adpcm.EncBytes(len(formatted.Data))))
|
b := bytes.NewBuffer(make([]byte, 0, adpcm.EncBytes(len(formatted.Data))))
|
||||||
enc := adpcm.NewEncoder(b)
|
enc := adpcm.NewEncoder(b)
|
||||||
_, err = enc.Write(formatted.Data)
|
_, err = enc.Write(formatted.Data)
|
|
@ -1,20 +1,43 @@
|
||||||
package revid
|
/*
|
||||||
|
NAME
|
||||||
|
audio_test.go
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Trek Hopton <trek@ausocean.org>
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
This file is Copyright (C) 2019 the Australian Ocean Lab (AusOcean)
|
||||||
|
|
||||||
|
It is free software: you can redistribute it and/or modify them
|
||||||
|
under the terms of the GNU General Public License as published by the
|
||||||
|
Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License in gpl.txt.
|
||||||
|
If not, see [GNU licenses](http://www.gnu.org/licenses).
|
||||||
|
*/
|
||||||
|
|
||||||
|
package audio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/codec/codecutil"
|
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||||
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
"github.com/yobert/alsa"
|
"github.com/yobert/alsa"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check that a device exists with the given config parameters.
|
// Check that a device exists with the given config parameters.
|
||||||
func checkDevice(ac *AudioConfig) error {
|
func checkDevice(ac *Config) error {
|
||||||
cards, err := alsa.OpenCards()
|
cards, err := alsa.OpenCards()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("no audio cards found")
|
return errors.New("no audio cards found")
|
||||||
|
@ -89,35 +112,14 @@ func checkDevice(ac *AudioConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// rTestLogger implements a revid.Logger.
|
func TestDevice(t *testing.T) {
|
||||||
type rTestLogger struct{}
|
|
||||||
|
|
||||||
func (tl rTestLogger) SetLevel(level int8) {}
|
|
||||||
|
|
||||||
func (tl rTestLogger) Log(level int8, msg string, params ...interface{}) {
|
|
||||||
logLevels := [...]string{"Debug", "Info", "Warn", "Error", "", "", "Fatal"}
|
|
||||||
if level < -1 || level > 5 {
|
|
||||||
panic("Invalid log level")
|
|
||||||
}
|
|
||||||
if !silent {
|
|
||||||
fmt.Printf("%s: %s\n", logLevels[level+1], msg)
|
|
||||||
}
|
|
||||||
if level == 5 {
|
|
||||||
buf := make([]byte, 1<<16)
|
|
||||||
size := runtime.Stack(buf, true)
|
|
||||||
fmt.Printf("%s\n", string(buf[:size]))
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAudioDevice(t *testing.T) {
|
|
||||||
// We want to open a device with a standard configuration.
|
// We want to open a device with a standard configuration.
|
||||||
ac := &AudioConfig{
|
ac := &Config{
|
||||||
SampleRate: 8000,
|
SampleRate: 8000,
|
||||||
Channels: 1,
|
Channels: 1,
|
||||||
RecPeriod: 0.5,
|
RecPeriod: 0.3,
|
||||||
BitDepth: 16,
|
BitDepth: 16,
|
||||||
Codec: ADPCM,
|
Codec: codecutil.ADPCM,
|
||||||
}
|
}
|
||||||
n := 2 // Number of periods to wait while recording.
|
n := 2 // Number of periods to wait while recording.
|
||||||
|
|
||||||
|
@ -127,9 +129,9 @@ func TestAudioDevice(t *testing.T) {
|
||||||
t.Skip(err)
|
t.Skip(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new audioDevice, start, read/lex, and then stop it.
|
// Create a new audio Device, start, read/lex, and then stop it.
|
||||||
var l rTestLogger
|
l := logger.New(logger.Debug, os.Stderr)
|
||||||
ai, err := NewAudioDevice(ac, l)
|
ai, err := NewDevice(ac, l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -139,6 +141,6 @@ func TestAudioDevice(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
go codecutil.LexBytes(dst, ai, time.Duration(ac.RecPeriod*float64(time.Second)), ai.ChunkSize())
|
go codecutil.LexBytes(dst, ai, time.Duration(ac.RecPeriod*float64(time.Second)), ai.ChunkSize())
|
||||||
time.Sleep(time.Second * time.Duration(ac.RecPeriod) * time.Duration(n))
|
time.Sleep(time.Duration(ac.RecPeriod*float64(time.Second)) * time.Duration(n))
|
||||||
ai.Stop()
|
ai.Stop()
|
||||||
}
|
}
|
|
@ -28,6 +28,7 @@ package revid
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||||
"bitbucket.org/ausocean/utils/logger"
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -116,14 +117,9 @@ const (
|
||||||
NothingDefined = iota
|
NothingDefined = iota
|
||||||
Raspivid
|
Raspivid
|
||||||
V4L
|
V4L
|
||||||
H264Codec
|
|
||||||
Audio
|
Audio
|
||||||
File
|
File
|
||||||
Http
|
Http
|
||||||
H264
|
|
||||||
Mjpeg
|
|
||||||
PCM
|
|
||||||
ADPCM
|
|
||||||
None
|
None
|
||||||
Mpegts
|
Mpegts
|
||||||
Ffmpeg
|
Ffmpeg
|
||||||
|
@ -157,7 +153,7 @@ const (
|
||||||
defaultQuantizationMode = QuantizationOff
|
defaultQuantizationMode = QuantizationOff
|
||||||
defaultFramesPerClip = 1
|
defaultFramesPerClip = 1
|
||||||
httpFramesPerClip = 560
|
httpFramesPerClip = 560
|
||||||
defaultInputCodec = H264
|
defaultInputCodec = codecutil.H264
|
||||||
defaultVerbosity = logger.Error
|
defaultVerbosity = logger.Error
|
||||||
defaultRtpAddr = "localhost:6970"
|
defaultRtpAddr = "localhost:6970"
|
||||||
defaultBurstPeriod = 10 // Seconds
|
defaultBurstPeriod = 10 // Seconds
|
||||||
|
@ -166,7 +162,7 @@ const (
|
||||||
defaultExposure = "auto"
|
defaultExposure = "auto"
|
||||||
defaultAutoWhiteBalance = "auto"
|
defaultAutoWhiteBalance = "auto"
|
||||||
|
|
||||||
defaultAudioInputCodec = ADPCM
|
defaultAudioInputCodec = codecutil.ADPCM
|
||||||
defaultSampleRate = 48000
|
defaultSampleRate = 48000
|
||||||
defaultBitDepth = 16
|
defaultBitDepth = 16
|
||||||
defaultChannels = 1
|
defaultChannels = 1
|
||||||
|
@ -197,7 +193,7 @@ func (c *Config) Validate(r *Revid) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.InputCodec {
|
switch c.InputCodec {
|
||||||
case H264:
|
case codecutil.H264:
|
||||||
// FIXME(kortschak): This is not really what we want.
|
// FIXME(kortschak): This is not really what we want.
|
||||||
// Configuration really needs to be rethought here.
|
// Configuration really needs to be rethought here.
|
||||||
if c.Quantize && c.Quantization == 0 {
|
if c.Quantize && c.Quantization == 0 {
|
||||||
|
@ -208,12 +204,12 @@ func (c *Config) Validate(r *Revid) error {
|
||||||
return errors.New("bad bitrate and quantization combination for H264 input")
|
return errors.New("bad bitrate and quantization combination for H264 input")
|
||||||
}
|
}
|
||||||
|
|
||||||
case Mjpeg:
|
case codecutil.MJPEG:
|
||||||
if c.Quantization > 0 || c.Bitrate == 0 {
|
if c.Quantization > 0 || c.Bitrate == 0 {
|
||||||
return errors.New("bad bitrate or quantization for mjpeg input")
|
return errors.New("bad bitrate or quantization for mjpeg input")
|
||||||
}
|
}
|
||||||
case PCM, ADPCM:
|
case codecutil.PCM, codecutil.ADPCM:
|
||||||
case NothingDefined:
|
default:
|
||||||
switch c.Input {
|
switch c.Input {
|
||||||
case Audio:
|
case Audio:
|
||||||
c.Logger.Log(logger.Info, pkg+"input is audio but no codec defined, defaulting", "inputCodec", defaultAudioInputCodec)
|
c.Logger.Log(logger.Info, pkg+"input is audio but no codec defined, defaulting", "inputCodec", defaultAudioInputCodec)
|
||||||
|
@ -224,8 +220,6 @@ func (c *Config) Validate(r *Revid) error {
|
||||||
c.Logger.Log(logger.Info, pkg+"defaulting quantization", "quantization", defaultQuantization)
|
c.Logger.Log(logger.Info, pkg+"defaulting quantization", "quantization", defaultQuantization)
|
||||||
c.Quantization = defaultQuantization
|
c.Quantization = defaultQuantization
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
return errors.New("bad input codec defined in config")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Outputs == nil {
|
if c.Outputs == nil {
|
||||||
|
|
|
@ -45,6 +45,7 @@ import (
|
||||||
"bitbucket.org/ausocean/av/codec/h265"
|
"bitbucket.org/ausocean/av/codec/h265"
|
||||||
"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/input/audio"
|
||||||
"bitbucket.org/ausocean/av/protocol/rtcp"
|
"bitbucket.org/ausocean/av/protocol/rtcp"
|
||||||
"bitbucket.org/ausocean/av/protocol/rtp"
|
"bitbucket.org/ausocean/av/protocol/rtp"
|
||||||
"bitbucket.org/ausocean/av/protocol/rtsp"
|
"bitbucket.org/ausocean/av/protocol/rtsp"
|
||||||
|
@ -539,7 +540,7 @@ func (r *Revid) startRaspivid() (func() error, error) {
|
||||||
switch r.config.InputCodec {
|
switch r.config.InputCodec {
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("revid: invalid input codec: %v", r.config.InputCodec)
|
return nil, fmt.Errorf("revid: invalid input codec: %v", r.config.InputCodec)
|
||||||
case H264:
|
case codecutil.H264:
|
||||||
args = append(args,
|
args = append(args,
|
||||||
"--codec", "H264",
|
"--codec", "H264",
|
||||||
"--inline",
|
"--inline",
|
||||||
|
@ -548,7 +549,7 @@ func (r *Revid) startRaspivid() (func() error, error) {
|
||||||
if r.config.Quantize {
|
if r.config.Quantize {
|
||||||
args = append(args, "-qp", fmt.Sprint(r.config.Quantization))
|
args = append(args, "-qp", fmt.Sprint(r.config.Quantization))
|
||||||
}
|
}
|
||||||
case Mjpeg:
|
case codecutil.MJPEG:
|
||||||
args = append(args, "--codec", "MJPEG")
|
args = append(args, "--codec", "MJPEG")
|
||||||
}
|
}
|
||||||
r.config.Logger.Log(logger.Info, pkg+"raspivid args", "raspividArgs", strings.Join(args, " "))
|
r.config.Logger.Log(logger.Info, pkg+"raspivid args", "raspividArgs", strings.Join(args, " "))
|
||||||
|
@ -628,7 +629,7 @@ func (r *Revid) setupInputForFile() (func() error, error) {
|
||||||
// startAudioDevice is used to start capturing audio from an audio device and processing it.
|
// startAudioDevice is used to start capturing audio from an audio device and processing it.
|
||||||
func (r *Revid) startAudioDevice() (func() error, error) {
|
func (r *Revid) startAudioDevice() (func() error, error) {
|
||||||
// Create audio device.
|
// Create audio device.
|
||||||
ac := &AudioConfig{
|
ac := &audio.Config{
|
||||||
SampleRate: r.config.SampleRate,
|
SampleRate: r.config.SampleRate,
|
||||||
Channels: r.config.Channels,
|
Channels: r.config.Channels,
|
||||||
RecPeriod: r.config.RecPeriod,
|
RecPeriod: r.config.RecPeriod,
|
||||||
|
@ -640,15 +641,15 @@ func (r *Revid) startAudioDevice() (func() error, error) {
|
||||||
mts.Meta.Add("period", fmt.Sprintf("%.6f", r.config.RecPeriod))
|
mts.Meta.Add("period", fmt.Sprintf("%.6f", r.config.RecPeriod))
|
||||||
mts.Meta.Add("bitDepth", strconv.Itoa(r.config.BitDepth))
|
mts.Meta.Add("bitDepth", strconv.Itoa(r.config.BitDepth))
|
||||||
switch r.config.InputCodec {
|
switch r.config.InputCodec {
|
||||||
case PCM:
|
case codecutil.PCM:
|
||||||
mts.Meta.Add("codec", "pcm")
|
mts.Meta.Add("codec", "pcm")
|
||||||
case ADPCM:
|
case codecutil.ADPCM:
|
||||||
mts.Meta.Add("codec", "adpcm")
|
mts.Meta.Add("codec", "adpcm")
|
||||||
default:
|
default:
|
||||||
r.config.Logger.Log(logger.Fatal, pkg+"no audio codec set in config")
|
r.config.Logger.Log(logger.Fatal, pkg+"no audio codec set in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
ai, err := NewAudioDevice(ac, r.config.Logger)
|
ai, err := audio.NewDevice(ac, r.config.Logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.config.Logger.Log(logger.Fatal, pkg+"failed to create audio device", "error", err.Error())
|
r.config.Logger.Log(logger.Fatal, pkg+"failed to create audio device", "error", err.Error())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue