cmd/vidforward & revid & device & codec/codecutil: fixes bugs and improving stream quality

This commit is contained in:
Saxon Nelson-Milton 2022-09-14 07:44:35 +00:00
parent 46e97debd5
commit b312774b49
6 changed files with 105 additions and 13 deletions

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/json"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@ -33,7 +34,7 @@ const (
logMaxBackup = 10 logMaxBackup = 10
logMaxAge = 28 // days logMaxAge = 28 // days
logVerbosity = logging.Info logVerbosity = logging.Info
logSuppress = true logSuppress = false
) )
// Misc constants. // Misc constants.
@ -52,6 +53,7 @@ type forwardHandler struct {
} }
func (h *forwardHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *forwardHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.log.Debug("recv handler")
q := r.URL.Query() q := r.URL.Query()
ma := q.Get("ma") ma := q.Get("ma")
@ -79,6 +81,8 @@ func (h *forwardHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
resp := map[string]interface{}{"ma": ma, "V0": size}
mtsClip, err := io.ReadAll(r.Body) mtsClip, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
h.errorLogWrite(w, "could not read forward request body", "error", err) h.errorLogWrite(w, "could not read forward request body", "error", err)
@ -103,6 +107,14 @@ func (h *forwardHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
} }
// Return response to client as JSON
jsn, err := json.Marshal(resp)
if err != nil {
h.errorLogWrite(w, "could not get json for response", "error", err)
return
}
fmt.Fprint(w, string(jsn))
} }
// writeError writes an error code in JSON format and logs it if in debug mode. // writeError writes an error code in JSON format and logs it if in debug mode.
@ -128,8 +140,10 @@ func (h *forwardHandler) addActive(ma, url string) error {
Logger: h.log, Logger: h.log,
Input: config.InputManual, Input: config.InputManual,
InputCodec: codecutil.H264_AU, // h264 access unit i.e. h264 frame. InputCodec: codecutil.H264_AU, // h264 access unit i.e. h264 frame.
Outputs: []uint8{config.OutputRTMP}, Outputs: []uint8{config.OutputRTMP},
RTMPURL: url, RTMPURL: url,
LogLevel: logging.Debug,
FileFPS: 25,
} }
rv, err := revid.New(cfg, nil) rv, err := revid.New(cfg, nil)
@ -137,6 +151,10 @@ func (h *forwardHandler) addActive(ma, url string) error {
return fmt.Errorf("coult not initialise revid: %w", err) return fmt.Errorf("coult not initialise revid: %w", err)
} }
h.actives[ma] = rv h.actives[ma] = rv
err = rv.Start()
if err != nil {
return fmt.Errorf("could not start revid pipeline")
}
h.mu.Unlock() h.mu.Unlock()
return nil return nil
} }
@ -192,18 +210,20 @@ func main() {
log.Fatal(pkg + "could not initialise netsender client: " + err.Error()) log.Fatal(pkg + "could not initialise netsender client: " + err.Error())
} }
fh := &forwardHandler{log: log} fh := &forwardHandler{log: log, actives: map[string]*revid.Revid{}}
log.Debug("beginning main loop") log.Debug("beginning main loop")
go run(ns, log, netLog, fh) readySig := make(chan struct{})
go run(ns, log, netLog, fh, readySig)
<-readySig
http.Handle("/recv", fh) http.Handle("/recv", fh)
http.ListenAndServe(*host+":"+*port, nil) http.ListenAndServe(*host+":"+*port, nil)
} }
// run starts the main loop. This will run netsender on every pass of the loop // run starts the main loop. This will run netsender on every pass of the loop
// (sleeping inbetween), check vars, and if changed, update revid as appropriate. // (sleeping inbetween), check vars, and if changed, update revid as appropriate.
func run(ns *netsender.Sender, l logging.Logger, nl *netlogger.Logger, fh *forwardHandler) { func run(ns *netsender.Sender, l logging.Logger, nl *netlogger.Logger, fh *forwardHandler, sig chan struct{}) {
var vs int var vs int
for { for {
l.Debug("running netsender") l.Debug("running netsender")
@ -267,6 +287,12 @@ func run(ns *netsender.Sender, l logging.Logger, nl *netlogger.Logger, fh *forwa
} }
fh.removeInactives(macs) fh.removeInactives(macs)
select {
case <-sig:
default:
close(sig)
}
sleep(ns, l) sleep(ns, l)
} }
} }
@ -294,9 +320,9 @@ func isMac(m string) bool {
return false return false
} }
if (3-i)%3 != 0 { if (3-i)%3 != 0 {
continue continue
} }
_, err := strconv.ParseUint(m[i:i+2], 16, 64) _, err := strconv.ParseUint(m[i:i+2], 16, 64)
if err != nil { if err != nil {

View File

@ -82,12 +82,62 @@ func (l *ByteLexer) Lex(dst io.Writer, src io.Reader, d time.Duration) error {
} }
} }
// Noop assumes that for writing to src is blocked until the entire previous
// write to src is read, i.e. src is expected to connected to a pipe-like structure.
func Noop(dst io.Writer, src io.Reader, d time.Duration) error { func Noop(dst io.Writer, src io.Reader, d time.Duration) error {
r := io.TeeReader(src,dst) if d < 0 {
return fmt.Errorf("invalid delay: %v", d)
}
if d == 0 {
d = 40 * time.Millisecond
}
ticker := time.NewTicker(d)
defer ticker.Stop()
const checkDur = 500 * time.Millisecond
rateChkTicker := time.NewTicker(checkDur)
frameCh := make(chan []byte, 1000)
errCh := make(chan error)
go func() {
for {
toWrite := <-frameCh
_, err := dst.Write(toWrite)
if err != nil {
errCh <- fmt.Errorf("could not write to dst: %w", err)
}
<-ticker.C
select {
case <-rateChkTicker.C:
var adj int
const equilibrium = 500
if len(frameCh) > equilibrium {
adj = -2
} else if len(frameCh) < equilibrium {
adj = 2
}
d += time.Millisecond * time.Duration(adj)
ticker.Reset(d)
default:
}
}
}()
const maxFrameSize = 250000 // = 20kB
buf := make([]byte, maxFrameSize)
for { for {
_, err := io.ReadAll(r) n, err := src.Read(buf)
if err != nil { if err != nil {
return err return fmt.Errorf("could not read src: %w", err)
}
newFrame := make([]byte, n)
copy(newFrame, buf[:n])
frameCh <- newFrame
select {
case err := <-errCh:
return fmt.Errorf("error from output routine: %w", err)
default:
} }
} }
} }

View File

@ -73,3 +73,12 @@ func (me MultiError) Error() string {
} }
return fmt.Sprintf("%v", []error(me)) return fmt.Sprintf("%v", []error(me))
} }
type ManualInput struct {}
func NewManualInput() *ManualInput { return &ManualInput{} }
func (m *ManualInput) Read(p []byte) (int, error) { return 0, nil }
func (m *ManualInput) Name() string { return "ManualInput" }
func (m *ManualInput) Set(c config.Config) error { return nil }
func (m *ManualInput) Start() error { return nil }
func (m *ManualInput) Stop() error { return nil }
func (m *ManualInput) IsRunning() bool { return true }

View File

@ -336,7 +336,7 @@ var Variables = []struct {
}, },
Validate: func(c *Config) { Validate: func(c *Config) {
switch c.Input { switch c.Input {
case InputRaspivid, InputRaspistill, InputV4L, InputFile, InputAudio, InputRTSP: case InputRaspivid, InputRaspistill, InputV4L, InputFile, InputAudio, InputRTSP, InputManual:
default: default:
c.LogInvalidField(KeyInput, defaultInput) c.LogInvalidField(KeyInput, defaultInput)
c.Input = defaultInput c.Input = defaultInput

View File

@ -40,6 +40,7 @@ import (
"bitbucket.org/ausocean/av/codec/jpeg" "bitbucket.org/ausocean/av/codec/jpeg"
"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/device"
"bitbucket.org/ausocean/av/device/file" "bitbucket.org/ausocean/av/device/file"
"bitbucket.org/ausocean/av/device/geovision" "bitbucket.org/ausocean/av/device/geovision"
"bitbucket.org/ausocean/av/device/raspistill" "bitbucket.org/ausocean/av/device/raspistill"
@ -313,6 +314,9 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
err = r.setupAudio() err = r.setupAudio()
case config.InputManual: case config.InputManual:
r.cfg.Logger.Debug("using manual input")
r.input = device.NewManualInput()
err = r.setLexer(r.cfg.InputCodec, false)
r.pipeReader, r.pipeWriter = io.Pipe() r.pipeReader, r.pipeWriter = io.Pipe()
default: default:
@ -392,7 +396,7 @@ func (r *Revid) processFrom(delay time.Duration) {
w = ioext.MultiWriteCloser(r.filters[0], r.probe) w = ioext.MultiWriteCloser(r.filters[0], r.probe)
} }
var err error var err error
if r.cfg.Input == config.InputManual { if r.cfg.Input == config.InputManual {
err = r.lexTo(w, r.pipeReader, delay) err = r.lexTo(w, r.pipeReader, delay)
} else { } else {

View File

@ -130,6 +130,9 @@ func (r *Revid) Bitrate() int {
} }
func (r *Revid) Write(p []byte) (int, error){ func (r *Revid) Write(p []byte) (int, error){
if r.pipeWriter == nil {
return 0, errors.New("revid input pipewriter not initialised, please start revid")
}
return r.pipeWriter.Write(p) return r.pipeWriter.Write(p)
} }