fixed merge conflicts

This commit is contained in:
Saxon 2020-08-14 17:25:02 +09:30
commit e3e81b9a91
11 changed files with 121 additions and 82 deletions

View File

@ -191,7 +191,7 @@ func TestEncodePcm(t *testing.T) {
sampleSize := 2 sampleSize := 2
blockSize := 16000 blockSize := 16000
writeFreq := float64(sampleRate*sampleSize) / float64(blockSize) 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 { if err != nil {
t.Fatalf("could not create MTS encoder, failed with error: %v", err) t.Fatalf("could not create MTS encoder, failed with error: %v", err)
} }

View File

@ -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 // 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 // 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. // 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 { return func(e *Encoder) error {
if r < 1 || r > 60 { if r < 1 || r > 60 {
return ErrInvalidRate return ErrInvalidRate
} }
e.writePeriod = time.Duration(float64(time.Second) / float64(r)) e.writePeriod = time.Duration(float64(time.Second) / r)
return nil return nil
} }
} }

View File

@ -49,7 +49,6 @@ const (
rbTimeout = 100 * time.Millisecond rbTimeout = 100 * time.Millisecond
rbNextTimeout = 2000 * time.Millisecond rbNextTimeout = 2000 * time.Millisecond
rbLen = 200 rbLen = 200
defaultSampleRate = 48000
) )
// "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.
@ -61,6 +60,23 @@ const (
stopped 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. // An ALSA device holds everything we need to know about the audio input stream and implements io.Reader and device.AVDevice.
type ALSA struct { type ALSA struct {
l Logger // Logger for device's routines to log to. l Logger // Logger for device's routines to log to.
@ -100,26 +116,31 @@ func (d *ALSA) Name() string {
return "ALSA" return "ALSA"
} }
// Set will take a Config struct, check the validity of the relevant fields // Setup will take a Config struct, check the validity of the relevant fields
// and then performs any configuration necessary. If fields are not valid, // and then perform any configuration necessary. If fields are not valid,
// an error is added to the multiError and a default value is used. // 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. // 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 var errs device.MultiError
if c.SampleRate <= 0 { 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 { 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 { 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 { 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) { if c.InputCodec != codecutil.ADPCM && c.InputCodec != codecutil.PCM {
errs = append(errs, errors.New("invalid codec")) errs = append(errs, errInvalidCodec)
c.InputCodec = defaultCodec
} }
d.Config = Config{ d.Config = Config{
SampleRate: c.SampleRate, SampleRate: c.SampleRate,
@ -164,6 +185,14 @@ func (d *ALSA) Set(c config.Config) error {
return nil 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. // 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. // Once an ALSA device has been stopped it cannot be started again. This is likely to change in future.
func (d *ALSA) Start() error { func (d *ALSA) Start() error {
@ -437,6 +466,12 @@ func (d *ALSA) formatBuffer() pcm.Buffer {
return formatted 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. // 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. // If the lower and higher power of two are the same distance, it returns the higher power.
// For negative values, 1 is returned. // For negative values, 1 is returned.

View File

@ -53,7 +53,7 @@ func TestDevice(t *testing.T) {
// Create a new ALSA device, start, read/lex, and then stop it. // Create a new ALSA device, start, read/lex, and then stop it.
l := logger.New(logger.Debug, os.Stderr, true) l := logger.New(logger.Debug, os.Stderr, true)
ai := New(l) ai := New(l)
err := ai.Set(c) err := ai.Setup(c)
// If there was an error opening the device, skip this test. // If there was an error opening the device, skip this test.
if _, ok := err.(OpenError); ok { if _, ok := err.(OpenError); ok {
t.Skip(err) t.Skip(err)
@ -119,7 +119,7 @@ func TestIsRunning(t *testing.T) {
l := logger.New(logger.Debug, &bytes.Buffer{}, true) // Discard logs. l := logger.New(logger.Debug, &bytes.Buffer{}, true) // Discard logs.
d := New(l) d := New(l)
err := d.Set(config.Config{ err := d.Setup(config.Config{
SampleRate: sampleRate, SampleRate: sampleRate,
Channels: channels, Channels: channels,
BitDepth: bitDepth, BitDepth: bitDepth,

View File

@ -29,13 +29,32 @@ import (
"strconv" "strconv"
"bitbucket.org/ausocean/av/codec/codecutil" "bitbucket.org/ausocean/av/codec/codecutil"
"bitbucket.org/ausocean/av/codec/pcm"
"bitbucket.org/ausocean/av/container/mts" "bitbucket.org/ausocean/av/container/mts"
"bitbucket.org/ausocean/av/device/alsa" "bitbucket.org/ausocean/av/device/alsa"
"bitbucket.org/ausocean/utils/logger" "bitbucket.org/ausocean/utils/logger"
) )
func (r *Revid) setupAudio() error { 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("sampleRate", strconv.Itoa(int(r.cfg.SampleRate)))
mts.Meta.Add("channels", strconv.Itoa(int(r.cfg.Channels))) mts.Meta.Add("channels", strconv.Itoa(int(r.cfg.Channels)))
mts.Meta.Add("period", fmt.Sprintf("%.6f", r.cfg.RecPeriod)) 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.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 return nil
} }

View File

@ -50,7 +50,6 @@ const (
InputAudio InputAudio
// Outputs. // Outputs.
OutputAudio
OutputRTMP OutputRTMP
OutputRTP OutputRTP
OutputHTTP OutputHTTP
@ -233,7 +232,6 @@ type Config struct {
VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input. VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input.
Width uint // Width defines the input video width 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 // Validate checks for any errors in the config fields and defaults settings

View File

@ -51,7 +51,6 @@ func TestValidate(t *testing.T) {
BurstPeriod: defaultBurstPeriod, BurstPeriod: defaultBurstPeriod,
MinFrames: defaultMinFrames, MinFrames: defaultMinFrames,
FrameRate: defaultFrameRate, FrameRate: defaultFrameRate,
WriteRate: defaultWriteRate,
ClipDuration: defaultClipDuration, ClipDuration: defaultClipDuration,
PSITime: defaultPSITime, PSITime: defaultPSITime,
FileFPS: defaultFileFPS, FileFPS: defaultFileFPS,

View File

@ -130,7 +130,6 @@ var params = []Param{
{N: "VBRQuality", BT: "uint8", E: []string{"Standard", "Fair", "Good", "Great", "Excellent"}}, {N: "VBRQuality", BT: "uint8", E: []string{"Standard", "Fair", "Good", "Great", "Excellent"}},
{N: "VerticalFlip", BT: "bool"}, {N: "VerticalFlip", BT: "bool"},
{N: "Width", BT: "uint", Min: 640, Max: 1920}, {N: "Width", BT: "uint", Min: 640, Max: 1920},
{N: "WriteRate", BT: "float64"},
} }
const fileHeader = ` const fileHeader = `

View File

@ -915,16 +915,3 @@ func (w *Width) Set(val string) error {
*w = Width(_v) *w = Width(_v)
return nil 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
}

View File

@ -49,9 +49,7 @@ const (
defaultBurstPeriod = 10 // Seconds defaultBurstPeriod = 10 // Seconds
defaultMinFrames = 100 defaultMinFrames = 100
defaultFrameRate = 25 defaultFrameRate = 25
defaultWriteRate = 25
defaultClipDuration = 0 defaultClipDuration = 0
defaultAudioInputCodec = codecutil.ADPCM
defaultPSITime = 2 defaultPSITime = 2
defaultFileFPS = 0 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", Name: "Exposure",
Type_: "enum:auto,night,nightpreview,backlight,spotlight,sports,snow,beach,verylong,fixedfps,antishake,fireworks", 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) { Validate: func(c *Config) {
switch c.InputCodec { switch c.InputCodec {
case codecutil.H264, codecutil.MJPEG, codecutil.PCM, codecutil.ADPCM: case codecutil.H264, codecutil.MJPEG, codecutil.PCM, codecutil.ADPCM:
default:
switch c.Input {
case OutputAudio:
c.LogInvalidField("InputCodec", defaultAudioInputCodec)
c.InputCodec = defaultAudioInputCodec
default: default:
c.LogInvalidField("InputCodec", defaultInputCodec) c.LogInvalidField("InputCodec", defaultInputCodec)
c.InputCodec = defaultInputCodec c.InputCodec = defaultInputCodec
} }
}
}, },
}, },
{ {
@ -449,6 +446,17 @@ var Variables = []struct {
c.RBWriteTimeout = lessThanOrEqual("RBWriteTimeout", c.RBWriteTimeout, 0, c, defaultRBWriteTimeout) 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", Name: "Rotation",
Type_: "uint", 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", Name: "Saturation",
Type_: "int", Type_: "int",
@ -522,14 +535,6 @@ var Variables = []struct {
Type_: "uint", Type_: "uint",
Update: func(c *Config, v string) { c.Width = parseUint("Width", v, c) }, 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 { func parseUint(n, v string, c *Config) uint {

View File

@ -169,7 +169,7 @@ func (r *Revid) reset(c config.Config) error {
r.cfg.Logger.Log(logger.Debug, "setting up revid pipeline") r.cfg.Logger.Log(logger.Debug, "setting up revid pipeline")
err = r.setupPipeline( 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 st int
var encOptions []func(*mts.Encoder) error var encOptions []func(*mts.Encoder) error
@ -211,10 +211,12 @@ func (r *Revid) reset(c config.Config) error {
} }
case config.InputAudio: case config.InputAudio:
st = mts.EncodeAudio st = mts.EncodeAudio
encOptions = append(encOptions, mts.TimeBasedPSI(time.Duration(r.cfg.PSITime)*time.Second))
rate = 1 / r.cfg.RecPeriod
default: default:
panic("unknown input type") 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...) return mts.NewEncoder(dst, &encLog{r.cfg.Logger}, encOptions...)
}, },
func(dst io.WriteCloser, fps int) (io.WriteCloser, error) { 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. // as a destination.
if len(mtsSenders) != 0 { if len(mtsSenders) != 0 {
mw := multiWriter(mtsSenders...) mw := multiWriter(mtsSenders...)
e, _ := mtsEnc(mw, r.cfg.WriteRate) e, _ := mtsEnc(mw, float64(r.cfg.FrameRate))
encoders = append(encoders, e) encoders = append(encoders, e)
} }
@ -454,9 +456,12 @@ func (r *Revid) Start() error {
} }
r.cfg.Logger.Log(logger.Info, "revid reset") 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) 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 d = time.Duration(1000/r.cfg.FileFPS) * time.Millisecond
} }