From a4a409b4f59fea99b05826f1477e3d11cb415f39 Mon Sep 17 00:00:00 2001 From: Saxon Nelson-Milton Date: Sun, 20 Dec 2020 12:03:43 +1030 Subject: [PATCH] exp/rvcl: adding default config option and opening VLC in the case of RTP output If no config is given as arguments or file, then a default config is used. This is file input (25 FPS) with MPEG-TS packetization and RTP output. If RTP output is selected, a VLC window to receive the stream is opened. --- exp/rvcl/main.go | 123 +++++++++++++++++++++++++++++++++-------------- go.mod | 2 +- go.sum | 2 + 3 files changed, 91 insertions(+), 36 deletions(-) diff --git a/exp/rvcl/main.go b/exp/rvcl/main.go index ed1e1b97..79a298c3 100644 --- a/exp/rvcl/main.go +++ b/exp/rvcl/main.go @@ -60,12 +60,10 @@ package main import ( "encoding/json" "flag" - "fmt" "os" + "os/exec" "runtime/pprof" - "gopkg.in/natefinch/lumberjack.v2" - "bitbucket.org/ausocean/av/container/mts" "bitbucket.org/ausocean/av/container/mts/meta" "bitbucket.org/ausocean/av/revid" @@ -82,12 +80,8 @@ const ( // Logging configuration. const ( - logMaxSize = 500 // MB - logMaxBackups = 10 - logMaxAge = 28 // days - logLevel = logger.Debug - logPath = "/var/log/netsender/netsender.log" - logSuppress = false + logLevel = logger.Info + logSuppress = true ) // Misc consts. @@ -96,15 +90,56 @@ const ( profilePath = "rvcl.prof" ) +// Netsender conf consts. +const ( + cfgPath = "/etc/netsender.conf" + fMode = 0777 +) + // canProfile is set to false with revid-cli is built with "-tags profile". var canProfile = false // The logger that will be used throughout. var log *logger.Logger +// stdoutLogger provides an io.Writer for the purpose of capturing stdout from +// the VLC process and using the logger to capture and print to stdout of +// this process. +type stdoutLogger struct { + l *logger.Logger + t string +} + +func (sl *stdoutLogger) Write(d []byte) (int, error) { + sl.l.Info(sl.t + ": " + string(d)) + return len(d), nil +} + +// stderrLogger provides an io.Writer for the purpose of capturing stderr from +// the VLC process and using the logger to capture and print to stdout of +// this process. +type stderrLogger struct { + l *logger.Logger + t string +} + +func (sl *stderrLogger) Write(d []byte) (int, error) { + sl.l.Error(sl.t + ": " + string(d)) + return len(d), nil +} + func main() { mts.Meta = meta.NewWith([][2]string{{metaPreambleKey, metaPreambleData}}) + // Create logger that methods will be called on by the netsender client and + // revid to log messages. Logs will go the lumberjack logger to handle file + // writing of messages. + log = logger.New( + logLevel, + os.Stdout, + logSuppress, + ) + // If built with profile tag, we will start CPU profiling. if canProfile { profile() @@ -115,6 +150,7 @@ func main() { var ( configPtr = flag.String("config", "", "Provide configuration JSON to revid (see readme for further information).") configFilePtr = flag.String("config-file", "", "Location of revid configuration file (see readme for further information).") + rtpAddrPtr = flag.String("addr", "localhost:6970", "RTP destination address (:)(common port=6970)") ) flag.Parse() @@ -124,62 +160,79 @@ func main() { err error ) switch { - case *configPtr != "" && *configFilePtr != "": // This doesn't make sense so panic. + // This doesn't make sense so panic. + case *configPtr != "" && *configFilePtr != "": panic("cannot define both command-line config and file config") - case *configPtr != "": // Decode JSON file to map. + + // Decode JSON file to map. + case *configPtr != "": err = json.Unmarshal([]byte(*configPtr), &cfg) if err != nil { - panic(fmt.Sprintf("could not decode JSON config: %v", err)) + log.Fatal("could not decode JSON config", "error", err) } - case *configFilePtr != "": // Decode JSON string to map from command line flag. + + // Decode JSON string to map from command line flag. + case *configFilePtr != "": f, err := os.Open(*configFilePtr) if err != nil { - panic(fmt.Sprintf("could not open config file: %v", err)) + log.Fatal("could not open config file", "error", err) } err = json.NewDecoder(f).Decode(&cfg) if err != nil { - panic(fmt.Sprintf("could not decode JSON config: %v", err)) + log.Fatal("could not decode JSON config", "error", err) } - default: // No config information has been provided; give empty map to force defaults. - cfg = map[string]string{} - } - // Create logger that methods will be called on by the netsender client and - // revid to log messages. Logs will go the lumberjack logger to handle file - // writing of messages. - log = logger.New( - logLevel, - &lumberjack.Logger{ - Filename: logPath, - MaxSize: logMaxSize, // MB - MaxBackups: logMaxBackups, - MaxAge: logMaxAge, // days - }, - logSuppress, - ) + // No config information has been provided; provide a default config map. + default: + cfg = map[string]string{ + "Input": "File", + "InputPath": "../../../test/test-data/av/input/betterInput.h264", + "FileFPS": "25", + "Output": "RTP", + "RTPAddress": *rtpAddrPtr, + "Loop": "true", + } + } + log.Info("got config", "config", cfg) // Create a netsender client. This is used only for HTTP sending of media // in this binary. ns, err := netsender.New(log, nil, nil, nil, nil) if err != nil { - log.Log(logger.Fatal, pkg+"could not initialise netsender client: "+err.Error()) + log.Fatal("could not initialise netsender client", "error", err) } + // Create the revid client, responsible for media collection and processing. + log.Info("got creating revid client") rv, err := revid.New(config.Config{Logger: log}, ns) if err != nil { - panic(fmt.Sprintf("could not create revid: %v", err)) + log.Fatal("could not create revid", "error", err) } // Configure revid with configuration map obtained through flags or file. // If config is empty, defaults will be adopted by revid. + log.Info("updating revid with config") err = rv.Update(cfg) if err != nil { - panic(fmt.Sprintf("could not update revid config: %v", err)) + log.Fatal("could not update revid config", "error", err) } + log.Info("starting revid") err = rv.Start() if err != nil { - panic(fmt.Sprintf("could not start revid: %v", err)) + log.Fatal("could not start revid", "error", err) + } + + // If output is RTP, open up a VLC window to see stream. + if v, ok := cfg["Output"]; ok && v == "RTP" { + log.Info("opening vlc window") + cmd := exec.Command("vlc", "rtp://"+*rtpAddrPtr) + cmd.Stdout = &stdoutLogger{log, "VLC STDOUT"} + cmd.Stderr = &stderrLogger{log, "VLC STDERR"} + err = cmd.Start() + if err != nil { + log.Fatal("could not run vlc command", "error", err) + } } // Run indefinitely. diff --git a/go.mod b/go.mod index 8e6dd73a..7b6d86c9 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.13 require ( bitbucket.org/ausocean/iot v1.3.0 - bitbucket.org/ausocean/utils v1.2.14 + bitbucket.org/ausocean/utils v1.2.15 github.com/Comcast/gots v0.0.0-20190305015453-8d56e473f0f7 github.com/go-audio/audio v0.0.0-20181013203223-7b2a6ca21480 github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884 diff --git a/go.sum b/go.sum index fc9f7077..542785d5 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ bitbucket.org/ausocean/utils v1.2.11 h1:zA0FOaPjN960ryp8PKCkV5y50uWBYrIxCVnXjwbv bitbucket.org/ausocean/utils v1.2.11/go.mod h1:uXzX9z3PLemyURTMWRhVI8uLhPX4uuvaaO85v2hcob8= bitbucket.org/ausocean/utils v1.2.14 h1:v5eBYavkEqKOBCppR6P451eT9UT/CQReMsOZZBUPX3Q= bitbucket.org/ausocean/utils v1.2.14/go.mod h1:uXzX9z3PLemyURTMWRhVI8uLhPX4uuvaaO85v2hcob8= +bitbucket.org/ausocean/utils v1.2.15 h1:Pz99ZfobdhACTtU6oj9BTyBcNSQulLvPT7wq4P343Es= +bitbucket.org/ausocean/utils v1.2.15/go.mod h1:uXzX9z3PLemyURTMWRhVI8uLhPX4uuvaaO85v2hcob8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Comcast/gots v0.0.0-20190305015453-8d56e473f0f7 h1:LdOc9B9Bj6LEsKiXShkLA3/kpxXb6LJpH+ekU2krbzw=