mirror of https://bitbucket.org/ausocean/av.git
revid: added basic disk and file size checking
* revid: reordered the file write method to exit earlier and remove a flag that wasn't needed * revid: add comments to MaxFileSize to specify that it is in bytes Approved-by: Alan Noble Approved-by: Saxon Milton
This commit is contained in:
parent
a2aaa605fd
commit
a266587397
|
@ -187,8 +187,9 @@ type Config struct {
|
||||||
// logging.Info, logging.Warning logging.Error, logging.Fatal.
|
// logging.Info, logging.Warning logging.Error, logging.Fatal.
|
||||||
LogLevel int8
|
LogLevel int8
|
||||||
|
|
||||||
Loop bool // If true will restart reading of input after an io.EOF.
|
Loop bool // If true will restart reading of input after an io.EOF.
|
||||||
MinFPS uint // The reduced framerate of the video when there is no motion.
|
MaxFileSize uint // Maximum size in bytes that a file will be written when File output is to be used. A value of 0 means unlimited.
|
||||||
|
MinFPS uint // The reduced framerate of the video when there is no motion.
|
||||||
|
|
||||||
// MinFrames defines the frequency of key NAL units SPS, PPS and IDR in
|
// MinFrames defines the frequency of key NAL units SPS, PPS and IDR in
|
||||||
// number of NAL units. This will also determine the frequency of PSI if the
|
// number of NAL units. This will also determine the frequency of PSI if the
|
||||||
|
|
|
@ -65,6 +65,7 @@ const (
|
||||||
KeyISO = "ISO"
|
KeyISO = "ISO"
|
||||||
KeyLogging = "logging"
|
KeyLogging = "logging"
|
||||||
KeyLoop = "Loop"
|
KeyLoop = "Loop"
|
||||||
|
KeyMaxFileSize = "MaxFileSize"
|
||||||
KeyMinFPS = "MinFPS"
|
KeyMinFPS = "MinFPS"
|
||||||
KeyMinFrames = "MinFrames"
|
KeyMinFrames = "MinFrames"
|
||||||
KeyMode = "mode"
|
KeyMode = "mode"
|
||||||
|
@ -399,6 +400,11 @@ var Variables = []struct {
|
||||||
Type: typeBool,
|
Type: typeBool,
|
||||||
Update: func(c *Config, v string) { c.Loop = parseBool(KeyLoop, v, c) },
|
Update: func(c *Config, v string) { c.Loop = parseBool(KeyLoop, v, c) },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: KeyMaxFileSize,
|
||||||
|
Type: typeUint,
|
||||||
|
Update: func(c *Config, v string) { c.MaxFileSize = parseUint(KeyMaxFileSize, v, c) },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: KeyMinFPS,
|
Name: KeyMinFPS,
|
||||||
Type: typeUint,
|
Type: typeUint,
|
||||||
|
|
|
@ -200,7 +200,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
|
||||||
mtsSenders = append(mtsSenders, w)
|
mtsSenders = append(mtsSenders, w)
|
||||||
case config.OutputFile:
|
case config.OutputFile:
|
||||||
r.cfg.Logger.Debug("using File output")
|
r.cfg.Logger.Debug("using File output")
|
||||||
w, err := newFileSender(r.cfg.Logger, r.cfg.OutputPath, false)
|
w, err := newFileSender(r.cfg.Logger, r.cfg.OutputPath, false, r.cfg.MaxFileSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
|
||||||
case config.OutputFiles:
|
case config.OutputFiles:
|
||||||
r.cfg.Logger.Debug("using Files output")
|
r.cfg.Logger.Debug("using Files output")
|
||||||
pb := pool.NewBuffer(int(r.cfg.PoolStartElementSize), int(nElements), writeTimeout)
|
pb := pool.NewBuffer(int(r.cfg.PoolStartElementSize), int(nElements), writeTimeout)
|
||||||
fs, err := newFileSender(r.cfg.Logger, r.cfg.OutputPath, true)
|
fs, err := newFileSender(r.cfg.Logger, r.cfg.OutputPath, true, r.cfg.MaxFileSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Comcast/gots/packet"
|
"github.com/Comcast/gots/packet"
|
||||||
|
@ -189,39 +190,77 @@ func extractMeta(r string, log logging.Logger) error {
|
||||||
|
|
||||||
// fileSender implements loadSender for a local file destination.
|
// fileSender implements loadSender for a local file destination.
|
||||||
type fileSender struct {
|
type fileSender struct {
|
||||||
file *os.File
|
file *os.File
|
||||||
data []byte
|
data []byte
|
||||||
multiFile bool
|
multiFile bool
|
||||||
path string
|
maxFileSize uint // maxFileSize is in bytes. A size of 0 means there is no size limit.
|
||||||
init bool
|
path string
|
||||||
log logging.Logger
|
log logging.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFileSender returns a new fileSender. Setting multi true will write a new
|
// newFileSender returns a new fileSender. Setting multi true will write a new
|
||||||
// file for each write to this sender.
|
// file for each write to this sender.
|
||||||
func newFileSender(l logging.Logger, path string, multiFile bool) (*fileSender, error) {
|
func newFileSender(l logging.Logger, path string, multiFile bool, maxFileSize uint) (*fileSender, error) {
|
||||||
return &fileSender{
|
return &fileSender{
|
||||||
path: path,
|
path: path,
|
||||||
log: l,
|
log: l,
|
||||||
multiFile: multiFile,
|
multiFile: multiFile,
|
||||||
init: true,
|
maxFileSize: maxFileSize,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write implements io.Writer.
|
// Write implements io.Writer.
|
||||||
func (s *fileSender) Write(d []byte) (int, error) {
|
func (s *fileSender) Write(d []byte) (int, error) {
|
||||||
if s.init || s.multiFile {
|
s.log.Debug("checking disk space")
|
||||||
fileName := s.path + time.Now().String()
|
var stat syscall.Statfs_t
|
||||||
s.log.Debug("creating new output file", "init", s.init, "multiFile", s.multiFile, "fileName", fileName)
|
if err := syscall.Statfs("/", &stat); err != nil {
|
||||||
|
return 0, fmt.Errorf("could not read system disk space, abandoning write: %w", err)
|
||||||
|
}
|
||||||
|
availableSpace := stat.Bavail * uint64(stat.Bsize)
|
||||||
|
totalSpace := stat.Blocks * uint64(stat.Bsize)
|
||||||
|
s.log.Debug("available, total disk space in bytes", "availableSpace", availableSpace, "totalSpace", totalSpace)
|
||||||
|
var spaceBuffer uint64 = 50000000 // 50MB.
|
||||||
|
if availableSpace < spaceBuffer {
|
||||||
|
return 0, fmt.Errorf("reached limit of disk space with a buffer of %v bytes, abandoning write", spaceBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the write will exceed the max file size, close the file so that a new one can be created.
|
||||||
|
if s.maxFileSize != 0 && s.file != nil {
|
||||||
|
fileInfo, err := s.file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("could not read files stats: %w", err)
|
||||||
|
}
|
||||||
|
size := uint(fileInfo.Size())
|
||||||
|
s.log.Debug("checked file size", "bytes", size)
|
||||||
|
if size+uint(len(d)) > s.maxFileSize {
|
||||||
|
s.log.Debug("new write would exceed max file size, closing existing file", "maxFileSize", s.maxFileSize)
|
||||||
|
s.file.Close()
|
||||||
|
s.file = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.file == nil {
|
||||||
|
fileName := s.path + time.Now().Format("2006-01-02_15-04-05")
|
||||||
|
s.log.Debug("creating new output file", "multiFile", s.multiFile, "fileName", fileName)
|
||||||
f, err := os.Create(fileName)
|
f, err := os.Create(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("could not create file to write media to: %w", err)
|
return 0, fmt.Errorf("could not create file to write media to: %w", err)
|
||||||
}
|
}
|
||||||
s.file = f
|
s.file = f
|
||||||
s.init = false
|
|
||||||
}
|
}
|
||||||
s.log.Debug("writing output file", "len(d)", len(d))
|
|
||||||
return s.file.Write(d)
|
s.log.Debug("writing to output file", "bytes", len(d))
|
||||||
|
n, err := s.file.Write(d)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.multiFile {
|
||||||
|
s.file.Close()
|
||||||
|
s.file = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fileSender) Close() error { return s.file.Close() }
|
func (s *fileSender) Close() error { return s.file.Close() }
|
||||||
|
|
Loading…
Reference in New Issue