mirror of https://bitbucket.org/ausocean/av.git
fixed merge conflicts
This commit is contained in:
commit
e3e81b9a91
|
@ -191,7 +191,7 @@ func TestEncodePcm(t *testing.T) {
|
|||
sampleSize := 2
|
||||
blockSize := 16000
|
||||
writeFreq := float64(sampleRate*sampleSize) / float64(blockSize)
|
||||
e, err := NewEncoder(nopCloser{&buf}, (*testLogger)(t), PacketBasedPSI(10), Rate(int(writeFreq)), MediaType(EncodeAudio))
|
||||
e, err := NewEncoder(nopCloser{&buf}, (*testLogger)(t), PacketBasedPSI(10), Rate(writeFreq), MediaType(EncodeAudio))
|
||||
if err != nil {
|
||||
t.Fatalf("could not create MTS encoder, failed with error: %v", err)
|
||||
}
|
||||
|
|
|
@ -97,12 +97,12 @@ func MediaType(mt int) func(*Encoder) error {
|
|||
// Rate is an option that can be passed to NewEncoder. It is used to specifiy
|
||||
// the rate at which the access units should be played in playback. This will
|
||||
// be used to create timestamps and counts such as PTS and PCR.
|
||||
func Rate(r int) func(*Encoder) error {
|
||||
func Rate(r float64) func(*Encoder) error {
|
||||
return func(e *Encoder) error {
|
||||
if r < 1 || r > 60 {
|
||||
return ErrInvalidRate
|
||||
}
|
||||
e.writePeriod = time.Duration(float64(time.Second) / float64(r))
|
||||
e.writePeriod = time.Duration(float64(time.Second) / r)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,6 @@ const (
|
|||
rbTimeout = 100 * time.Millisecond
|
||||
rbNextTimeout = 2000 * time.Millisecond
|
||||
rbLen = 200
|
||||
defaultSampleRate = 48000
|
||||
)
|
||||
|
||||
// "running" means the input goroutine is reading from the ALSA device and writing to the ringbuffer.
|
||||
|
@ -61,6 +60,23 @@ const (
|
|||
stopped
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSampleRate = 48000
|
||||
defaultBitDepth = 16
|
||||
defaultChannels = 1
|
||||
defaultRecPeriod = 1.0
|
||||
defaultCodec = codecutil.PCM
|
||||
)
|
||||
|
||||
// Configuration field errors.
|
||||
var (
|
||||
errInvalidSampleRate = errors.New("invalid sample rate, defaulting")
|
||||
errInvalidChannels = errors.New("invalid number of channels, defaulting")
|
||||
errInvalidBitDepth = errors.New("invalid bitdepth, defaulting")
|
||||
errInvalidRecPeriod = errors.New("invalid record period, defaulting")
|
||||
errInvalidCodec = errors.New("invalid audio codec, defaulting")
|
||||
)
|
||||
|
||||
// An ALSA device holds everything we need to know about the audio input stream and implements io.Reader and device.AVDevice.
|
||||
type ALSA struct {
|
||||
l Logger // Logger for device's routines to log to.
|
||||
|
@ -100,26 +116,31 @@ func (d *ALSA) Name() string {
|
|||
return "ALSA"
|
||||
}
|
||||
|
||||
// Set will take a Config struct, check the validity of the relevant fields
|
||||
// and then performs any configuration necessary. If fields are not valid,
|
||||
// Setup will take a Config struct, check the validity of the relevant fields
|
||||
// and then perform any configuration necessary. If fields are not valid,
|
||||
// an error is added to the multiError and a default value is used.
|
||||
// It then initialises the ALSA device which can then be started, read from, and stopped.
|
||||
func (d *ALSA) Set(c config.Config) error {
|
||||
func (d *ALSA) Setup(c config.Config) error {
|
||||
var errs device.MultiError
|
||||
if c.SampleRate <= 0 {
|
||||
errs = append(errs, fmt.Errorf("invalid sample rate: %v", c.SampleRate))
|
||||
errs = append(errs, errInvalidSampleRate)
|
||||
c.SampleRate = defaultSampleRate
|
||||
}
|
||||
if c.Channels <= 0 {
|
||||
errs = append(errs, fmt.Errorf("invalid number of channels: %v", c.Channels))
|
||||
errs = append(errs, errInvalidChannels)
|
||||
c.Channels = defaultChannels
|
||||
}
|
||||
if c.BitDepth <= 0 {
|
||||
errs = append(errs, fmt.Errorf("invalid bitdepth: %v", c.BitDepth))
|
||||
errs = append(errs, errInvalidBitDepth)
|
||||
c.BitDepth = defaultBitDepth
|
||||
}
|
||||
if c.RecPeriod <= 0 {
|
||||
errs = append(errs, fmt.Errorf("invalid recording period: %v", c.RecPeriod))
|
||||
errs = append(errs, errInvalidRecPeriod)
|
||||
c.RecPeriod = defaultRecPeriod
|
||||
}
|
||||
if !codecutil.IsValid(c.InputCodec) {
|
||||
errs = append(errs, errors.New("invalid codec"))
|
||||
if c.InputCodec != codecutil.ADPCM && c.InputCodec != codecutil.PCM {
|
||||
errs = append(errs, errInvalidCodec)
|
||||
c.InputCodec = defaultCodec
|
||||
}
|
||||
d.Config = Config{
|
||||
SampleRate: c.SampleRate,
|
||||
|
@ -164,6 +185,14 @@ func (d *ALSA) Set(c config.Config) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Set exists to satisfy the implementation of the Device interface that revid uses.
|
||||
// Everything that would usually be in Set is in the Setup function.
|
||||
// This is because an ALSA device is different to other devices in that it
|
||||
// outputs binary non-packetised data and it requires a different configuration procedure.
|
||||
func (d *ALSA) Set(c config.Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start will start recording audio and writing to the ringbuffer.
|
||||
// Once an ALSA device has been stopped it cannot be started again. This is likely to change in future.
|
||||
func (d *ALSA) Start() error {
|
||||
|
@ -437,6 +466,12 @@ func (d *ALSA) formatBuffer() pcm.Buffer {
|
|||
return formatted
|
||||
}
|
||||
|
||||
// DataSize returns the size in bytes of the data ALSA device d will
|
||||
// output in the duration of a single recording period.
|
||||
func (d *ALSA) DataSize() int {
|
||||
return pcm.DataSize(d.SampleRate, d.Channels, d.BitDepth, d.RecPeriod, d.Codec)
|
||||
}
|
||||
|
||||
// nearestPowerOfTwo finds and returns the nearest power of two to the given integer.
|
||||
// If the lower and higher power of two are the same distance, it returns the higher power.
|
||||
// For negative values, 1 is returned.
|
||||
|
|
|
@ -53,7 +53,7 @@ func TestDevice(t *testing.T) {
|
|||
// Create a new ALSA device, start, read/lex, and then stop it.
|
||||
l := logger.New(logger.Debug, os.Stderr, true)
|
||||
ai := New(l)
|
||||
err := ai.Set(c)
|
||||
err := ai.Setup(c)
|
||||
// If there was an error opening the device, skip this test.
|
||||
if _, ok := err.(OpenError); ok {
|
||||
t.Skip(err)
|
||||
|
@ -119,7 +119,7 @@ func TestIsRunning(t *testing.T) {
|
|||
l := logger.New(logger.Debug, &bytes.Buffer{}, true) // Discard logs.
|
||||
d := New(l)
|
||||
|
||||
err := d.Set(config.Config{
|
||||
err := d.Setup(config.Config{
|
||||
SampleRate: sampleRate,
|
||||
Channels: channels,
|
||||
BitDepth: bitDepth,
|
||||
|
|
|
@ -29,13 +29,32 @@ import (
|
|||
"strconv"
|
||||
|
||||
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||
"bitbucket.org/ausocean/av/codec/pcm"
|
||||
"bitbucket.org/ausocean/av/container/mts"
|
||||
"bitbucket.org/ausocean/av/device/alsa"
|
||||
"bitbucket.org/ausocean/utils/logger"
|
||||
)
|
||||
|
||||
func (r *Revid) setupAudio() error {
|
||||
// Create new ALSA device.
|
||||
d := alsa.New(r.cfg.Logger)
|
||||
r.input = d
|
||||
|
||||
// Configure ALSA device.
|
||||
r.cfg.Logger.Log(logger.Debug, "configuring input device")
|
||||
err := d.Setup(r.cfg)
|
||||
if err != nil {
|
||||
r.cfg.Logger.Log(logger.Warning, "errors from configuring input device", "errors", err)
|
||||
}
|
||||
r.cfg.Logger.Log(logger.Info, "input device configured")
|
||||
|
||||
// Set revid's lexer.
|
||||
l, err := codecutil.NewByteLexer(d.DataSize())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.lexTo = l.Lex
|
||||
|
||||
// Add metadata.
|
||||
mts.Meta.Add("sampleRate", strconv.Itoa(int(r.cfg.SampleRate)))
|
||||
mts.Meta.Add("channels", strconv.Itoa(int(r.cfg.Channels)))
|
||||
mts.Meta.Add("period", fmt.Sprintf("%.6f", r.cfg.RecPeriod))
|
||||
|
@ -50,13 +69,5 @@ func (r *Revid) setupAudio() error {
|
|||
r.cfg.Logger.Log(logger.Fatal, "no audio codec set in config")
|
||||
}
|
||||
|
||||
r.input = alsa.New(r.cfg.Logger)
|
||||
|
||||
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
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ const (
|
|||
InputAudio
|
||||
|
||||
// Outputs.
|
||||
OutputAudio
|
||||
OutputRTMP
|
||||
OutputRTP
|
||||
OutputHTTP
|
||||
|
@ -233,7 +232,6 @@ type Config struct {
|
|||
|
||||
VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input.
|
||||
Width uint // Width defines the input video width Raspivid input.
|
||||
WriteRate float64 // WriteRate is how many times a second revid encoders will be written to.
|
||||
}
|
||||
|
||||
// Validate checks for any errors in the config fields and defaults settings
|
||||
|
|
|
@ -51,7 +51,6 @@ func TestValidate(t *testing.T) {
|
|||
BurstPeriod: defaultBurstPeriod,
|
||||
MinFrames: defaultMinFrames,
|
||||
FrameRate: defaultFrameRate,
|
||||
WriteRate: defaultWriteRate,
|
||||
ClipDuration: defaultClipDuration,
|
||||
PSITime: defaultPSITime,
|
||||
FileFPS: defaultFileFPS,
|
||||
|
|
|
@ -130,7 +130,6 @@ var params = []Param{
|
|||
{N: "VBRQuality", BT: "uint8", E: []string{"Standard", "Fair", "Good", "Great", "Excellent"}},
|
||||
{N: "VerticalFlip", BT: "bool"},
|
||||
{N: "Width", BT: "uint", Min: 640, Max: 1920},
|
||||
{N: "WriteRate", BT: "float64"},
|
||||
}
|
||||
|
||||
const fileHeader = `
|
||||
|
|
|
@ -915,16 +915,3 @@ func (w *Width) Set(val string) error {
|
|||
*w = Width(_v)
|
||||
return nil
|
||||
}
|
||||
|
||||
type WriteRate float64
|
||||
|
||||
func (w *WriteRate) Type() string { return "float64" }
|
||||
func (w *WriteRate) Set(val string) error {
|
||||
_v, err := strconv.ParseFloat(val, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not convert set string to float: %w", err)
|
||||
}
|
||||
|
||||
*w = WriteRate(_v)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -49,9 +49,7 @@ const (
|
|||
defaultBurstPeriod = 10 // Seconds
|
||||
defaultMinFrames = 100
|
||||
defaultFrameRate = 25
|
||||
defaultWriteRate = 25
|
||||
defaultClipDuration = 0
|
||||
defaultAudioInputCodec = codecutil.ADPCM
|
||||
defaultPSITime = 2
|
||||
defaultFileFPS = 0
|
||||
|
||||
|
@ -138,6 +136,11 @@ var Variables = []struct {
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Channels",
|
||||
Type_: "uint",
|
||||
Update: func(c *Config, v string) { c.Channels = parseUint("Channels", v, c) },
|
||||
},
|
||||
{
|
||||
Name: "Exposure",
|
||||
Type_: "enum:auto,night,nightpreview,backlight,spotlight,sports,snow,beach,verylong,fixedfps,antishake,fireworks",
|
||||
|
@ -242,16 +245,10 @@ var Variables = []struct {
|
|||
Validate: func(c *Config) {
|
||||
switch c.InputCodec {
|
||||
case codecutil.H264, codecutil.MJPEG, codecutil.PCM, codecutil.ADPCM:
|
||||
default:
|
||||
switch c.Input {
|
||||
case OutputAudio:
|
||||
c.LogInvalidField("InputCodec", defaultAudioInputCodec)
|
||||
c.InputCodec = defaultAudioInputCodec
|
||||
default:
|
||||
c.LogInvalidField("InputCodec", defaultInputCodec)
|
||||
c.InputCodec = defaultInputCodec
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -449,6 +446,17 @@ var Variables = []struct {
|
|||
c.RBWriteTimeout = lessThanOrEqual("RBWriteTimeout", c.RBWriteTimeout, 0, c, defaultRBWriteTimeout)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "RecPeriod",
|
||||
Type_: "float",
|
||||
Update: func(c *Config, v string) {
|
||||
_v, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
c.Logger.Log(logger.Warning, fmt.Sprintf("invalid %s param", "RecPeriod"), "value", v)
|
||||
}
|
||||
c.RecPeriod = _v
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Rotation",
|
||||
Type_: "uint",
|
||||
|
@ -470,6 +478,11 @@ var Variables = []struct {
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "SampleRate",
|
||||
Type_: "uint",
|
||||
Update: func(c *Config, v string) { c.SampleRate = parseUint("SampleRate", v, c) },
|
||||
},
|
||||
{
|
||||
Name: "Saturation",
|
||||
Type_: "int",
|
||||
|
@ -522,14 +535,6 @@ var Variables = []struct {
|
|||
Type_: "uint",
|
||||
Update: func(c *Config, v string) { c.Width = parseUint("Width", v, c) },
|
||||
},
|
||||
{
|
||||
Name: "WriteRate",
|
||||
Type_: "uint",
|
||||
Update: func(c *Config, v string) { c.WriteRate = float64(parseUint("WriteRate", v, c)) },
|
||||
Validate: func(c *Config) {
|
||||
c.WriteRate = float64(lessThanOrEqual("WriteRate", uint(c.WriteRate), 0, c, defaultWriteRate))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func parseUint(n, v string, c *Config) uint {
|
||||
|
|
|
@ -169,7 +169,7 @@ func (r *Revid) reset(c config.Config) error {
|
|||
|
||||
r.cfg.Logger.Log(logger.Debug, "setting up revid pipeline")
|
||||
err = r.setupPipeline(
|
||||
func(dst io.WriteCloser, fps float64) (io.WriteCloser, error) {
|
||||
func(dst io.WriteCloser, rate float64) (io.WriteCloser, error) {
|
||||
var st int
|
||||
var encOptions []func(*mts.Encoder) error
|
||||
|
||||
|
@ -211,10 +211,12 @@ func (r *Revid) reset(c config.Config) error {
|
|||
}
|
||||
case config.InputAudio:
|
||||
st = mts.EncodeAudio
|
||||
encOptions = append(encOptions, mts.TimeBasedPSI(time.Duration(r.cfg.PSITime)*time.Second))
|
||||
rate = 1 / r.cfg.RecPeriod
|
||||
default:
|
||||
panic("unknown input type")
|
||||
}
|
||||
encOptions = append(encOptions, mts.MediaType(st), mts.Rate(int(fps)))
|
||||
encOptions = append(encOptions, mts.MediaType(st), mts.Rate(rate))
|
||||
return mts.NewEncoder(dst, &encLog{r.cfg.Logger}, encOptions...)
|
||||
},
|
||||
func(dst io.WriteCloser, fps int) (io.WriteCloser, error) {
|
||||
|
@ -310,7 +312,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
|
|||
// as a destination.
|
||||
if len(mtsSenders) != 0 {
|
||||
mw := multiWriter(mtsSenders...)
|
||||
e, _ := mtsEnc(mw, r.cfg.WriteRate)
|
||||
e, _ := mtsEnc(mw, float64(r.cfg.FrameRate))
|
||||
encoders = append(encoders, e)
|
||||
}
|
||||
|
||||
|
@ -454,9 +456,12 @@ func (r *Revid) Start() error {
|
|||
}
|
||||
r.cfg.Logger.Log(logger.Info, "revid reset")
|
||||
|
||||
// Calculate delay between frames based on FileFPS.
|
||||
// Calculate delay between frames based on FileFPS for video or
|
||||
// between recording periods for audio.
|
||||
d := time.Duration(0)
|
||||
if r.cfg.FileFPS != 0 {
|
||||
if r.cfg.Input == config.InputAudio {
|
||||
d = time.Duration(r.cfg.RecPeriod * float64(time.Second))
|
||||
} else if r.cfg.FileFPS != 0 {
|
||||
d = time.Duration(1000/r.cfg.FileFPS) * time.Millisecond
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue