mirror of https://bitbucket.org/ausocean/av.git
Merged in av-logging-update (pull request #57)
av-logging-update Approved-by: kortschak <dan@kortschak.io>
This commit is contained in:
commit
be1bf3ef77
|
@ -30,8 +30,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -47,7 +45,7 @@ const (
|
||||||
progName = "revid-cli"
|
progName = "revid-cli"
|
||||||
|
|
||||||
// Logging is set to INFO level.
|
// Logging is set to INFO level.
|
||||||
loggerVerbosity = 3
|
defaultLogVerbosity = smartlogger.Info
|
||||||
)
|
)
|
||||||
|
|
||||||
// Indexes for configFlags
|
// Indexes for configFlags
|
||||||
|
@ -73,6 +71,7 @@ const (
|
||||||
intraRefreshPeriodPtr
|
intraRefreshPeriodPtr
|
||||||
verticalFlipPtr
|
verticalFlipPtr
|
||||||
horizontalFlipPtr
|
horizontalFlipPtr
|
||||||
|
logPathPtr
|
||||||
|
|
||||||
noOfConfigFlags
|
noOfConfigFlags
|
||||||
)
|
)
|
||||||
|
@ -82,21 +81,29 @@ const (
|
||||||
netSendRetryTime = 5 * time.Second
|
netSendRetryTime = 5 * time.Second
|
||||||
defaultRunDuration = 24 * time.Hour
|
defaultRunDuration = 24 * time.Hour
|
||||||
revidStopTime = 5 * time.Second
|
revidStopTime = 5 * time.Second
|
||||||
|
defaultLogPath = "/var/log/netsender/"
|
||||||
|
pkg = "revid-cli:"
|
||||||
)
|
)
|
||||||
|
|
||||||
// canProfile is set to false with revid-cli is built with "-tags profile".
|
// canProfile is set to false with revid-cli is built with "-tags profile".
|
||||||
var canProfile = true
|
var canProfile = true
|
||||||
|
|
||||||
|
// The logger that will be used throughout
|
||||||
|
var logger *smartlogger.Logger
|
||||||
|
|
||||||
// Globals
|
// Globals
|
||||||
var (
|
var (
|
||||||
rv *revid.Revid
|
rv *revid.Revid
|
||||||
config revid.Config
|
config revid.Config
|
||||||
|
configFlags = make([](*string), noOfConfigFlags)
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
// Flags
|
||||||
cpuprofile := flag.String("cpuprofile", "", "write cpu profile to `file`")
|
var (
|
||||||
|
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
||||||
flagNames := [noOfConfigFlags]struct{ name, description string }{
|
useNetsender = flag.Bool("NetSender", false, "Are we checking vars through netsender?")
|
||||||
|
runDurationPtr = flag.Duration("runDuration", defaultRunDuration, "How long do you want revid to run for?")
|
||||||
|
flagNames = [noOfConfigFlags]struct{ name, description string }{
|
||||||
{"Input", "The input type: Raspivid, File"},
|
{"Input", "The input type: Raspivid, File"},
|
||||||
{"InputCodec", "The codec of the input: H264, Mjpeg"},
|
{"InputCodec", "The codec of the input: H264, Mjpeg"},
|
||||||
{"Output", "The output type: Http, Rtmp, File"},
|
{"Output", "The output type: Http, Rtmp, File"},
|
||||||
|
@ -104,7 +111,7 @@ func main() {
|
||||||
// NOTE: we add rtp here when we have this functionality
|
// NOTE: we add rtp here when we have this functionality
|
||||||
{"Packetization", "The method of data packetisation: Flv, Mpegts, None"},
|
{"Packetization", "The method of data packetisation: Flv, Mpegts, None"},
|
||||||
{"QuantizationMode", "Whether quantization if on or off (variable bitrate): On, Off"},
|
{"QuantizationMode", "Whether quantization if on or off (variable bitrate): On, Off"},
|
||||||
{"Verbosity", "Verbosity: On, Off"},
|
{"Verbosity", "Verbosity: Info, Warning, Error, Fatal"},
|
||||||
{"FramesPerClip", "Number of frames per clip sent"},
|
{"FramesPerClip", "Number of frames per clip sent"},
|
||||||
{"RtmpUrl", "Url of rtmp endpoint"},
|
{"RtmpUrl", "Url of rtmp endpoint"},
|
||||||
{"Bitrate", "Bitrate of recorded video"},
|
{"Bitrate", "Bitrate of recorded video"},
|
||||||
|
@ -119,33 +126,59 @@ func main() {
|
||||||
{"IntraRefreshPeriod", "The IntraRefreshPeriod i.e. how many keyframes we send"},
|
{"IntraRefreshPeriod", "The IntraRefreshPeriod i.e. how many keyframes we send"},
|
||||||
{"VerticalFlip", "Flip video vertically: Yes, No"},
|
{"VerticalFlip", "Flip video vertically: Yes, No"},
|
||||||
{"HorizontalFlip", "Flip video horizontally: Yes, No"},
|
{"HorizontalFlip", "Flip video horizontally: Yes, No"},
|
||||||
|
{"LogPath", "Path for logging files (default is /var/log/netsender/)"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
handleFlags()
|
||||||
|
|
||||||
|
if !*useNetsender {
|
||||||
|
// run revid for the specified duration
|
||||||
|
startRevid(nil)
|
||||||
|
time.Sleep(*runDurationPtr)
|
||||||
|
stopRevid()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the configFlags based on the flagNames array
|
err := run()
|
||||||
configFlags := make([](*string), noOfConfigFlags)
|
if err != nil {
|
||||||
|
logger.Log(smartlogger.Fatal, pkg+"failed to run revid", "error", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle flags interprets and validates command line flags and sets revid
|
||||||
|
// config etc accordingly
|
||||||
|
func handleFlags() {
|
||||||
|
// Create the configFlags based on the flagNames struct array
|
||||||
for i, f := range &flagNames {
|
for i, f := range &flagNames {
|
||||||
configFlags[i] = flag.String(f.name, "", f.description)
|
configFlags[i] = flag.String(f.name, "", f.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we want a netsender session
|
|
||||||
useNetsender := flag.Bool("NetSender", false, "Are we checking vars through netsender?")
|
|
||||||
// User might also want to define how long revid runs for
|
|
||||||
runDurationPtr := flag.Duration("runDuration", defaultRunDuration, "How long do you want revid to run for?")
|
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
logPath := defaultLogPath
|
||||||
|
if *configFlags[logPathPtr] != "" {
|
||||||
|
logPath = *configFlags[logPathPtr]
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = smartlogger.New(defaultLogVerbosity, logPath)
|
||||||
|
|
||||||
|
config.Logger = logger
|
||||||
|
|
||||||
if *cpuprofile != "" {
|
if *cpuprofile != "" {
|
||||||
if canProfile {
|
if canProfile {
|
||||||
f, err := os.Create(*cpuprofile)
|
f, err := os.Create(*cpuprofile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("could not create CPU profile: ", err)
|
logger.Log(smartlogger.Fatal, pkg+"could not create CPU profile", "error", err.Error())
|
||||||
}
|
}
|
||||||
if err := pprof.StartCPUProfile(f); err != nil {
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
log.Fatal("could not start CPU profile: ", err)
|
logger.Log(smartlogger.Fatal, pkg+"could not start CPU profile", "error", err.Error())
|
||||||
}
|
}
|
||||||
defer pprof.StopCPUProfile()
|
defer pprof.StopCPUProfile()
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintln(os.Stderr, "Ignoring cpuprofile flag - http/pprof built in.")
|
logger.Log(smartlogger.Warning, pkg+"ignoring cpuprofile flag - http/pprof built in.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +189,7 @@ func main() {
|
||||||
config.Input = revid.File
|
config.Input = revid.File
|
||||||
case "":
|
case "":
|
||||||
default:
|
default:
|
||||||
fmt.Println("Bad input argument!")
|
logger.Log(smartlogger.Error, pkg+"bad input argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *configFlags[inputCodecPtr] {
|
switch *configFlags[inputCodecPtr] {
|
||||||
|
@ -164,7 +197,7 @@ func main() {
|
||||||
config.InputCodec = revid.H264Codec
|
config.InputCodec = revid.H264Codec
|
||||||
case "":
|
case "":
|
||||||
default:
|
default:
|
||||||
fmt.Println("Bad input codec argument!")
|
logger.Log(smartlogger.Error, pkg+"bad input codec argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *configFlags[outputPtr] {
|
switch *configFlags[outputPtr] {
|
||||||
|
@ -178,7 +211,7 @@ func main() {
|
||||||
config.Output = revid.FfmpegRtmp
|
config.Output = revid.FfmpegRtmp
|
||||||
case "":
|
case "":
|
||||||
default:
|
default:
|
||||||
fmt.Println("Bad output argument!")
|
logger.Log(smartlogger.Error, pkg+"bad output argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *configFlags[rtmpMethodPtr] {
|
switch *configFlags[rtmpMethodPtr] {
|
||||||
|
@ -188,7 +221,7 @@ func main() {
|
||||||
config.RtmpMethod = revid.LibRtmp
|
config.RtmpMethod = revid.LibRtmp
|
||||||
case "":
|
case "":
|
||||||
default:
|
default:
|
||||||
fmt.Println("Bad rtmp method argument!")
|
logger.Log(smartlogger.Error, pkg+"bad rtmp method argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *configFlags[packetizationPtr] {
|
switch *configFlags[packetizationPtr] {
|
||||||
|
@ -200,7 +233,7 @@ func main() {
|
||||||
config.Packetization = revid.Flv
|
config.Packetization = revid.Flv
|
||||||
case "":
|
case "":
|
||||||
default:
|
default:
|
||||||
fmt.Println("Bad packetization argument!")
|
logger.Log(smartlogger.Error, pkg+"bad packetization argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *configFlags[quantizationModePtr] {
|
switch *configFlags[quantizationModePtr] {
|
||||||
|
@ -210,7 +243,7 @@ func main() {
|
||||||
config.QuantizationMode = revid.QuantizationOff
|
config.QuantizationMode = revid.QuantizationOff
|
||||||
case "":
|
case "":
|
||||||
default:
|
default:
|
||||||
fmt.Println("Bad quantization mode argument!")
|
logger.Log(smartlogger.Error, pkg+"bad quantization mode argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *configFlags[verbosityPtr] {
|
switch *configFlags[verbosityPtr] {
|
||||||
|
@ -220,7 +253,7 @@ func main() {
|
||||||
config.Verbosity = revid.Yes
|
config.Verbosity = revid.Yes
|
||||||
case "":
|
case "":
|
||||||
default:
|
default:
|
||||||
fmt.Println("Bad verbosity argument!")
|
logger.Log(smartlogger.Error, pkg+"bad verbosity argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *configFlags[horizontalFlipPtr] {
|
switch *configFlags[horizontalFlipPtr] {
|
||||||
|
@ -231,7 +264,7 @@ func main() {
|
||||||
case "":
|
case "":
|
||||||
config.HorizontalFlip = revid.No
|
config.HorizontalFlip = revid.No
|
||||||
default:
|
default:
|
||||||
fmt.Println("Bad horizontal flip option!")
|
logger.Log(smartlogger.Error, pkg+"bad horizontal flip option")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *configFlags[verticalFlipPtr] {
|
switch *configFlags[verticalFlipPtr] {
|
||||||
|
@ -242,7 +275,7 @@ func main() {
|
||||||
case "":
|
case "":
|
||||||
config.VerticalFlip = revid.No
|
config.VerticalFlip = revid.No
|
||||||
default:
|
default:
|
||||||
fmt.Println("Bad vertical flip option!")
|
logger.Log(smartlogger.Error, pkg+"bad vertical flip option")
|
||||||
}
|
}
|
||||||
|
|
||||||
fpc, err := strconv.Atoi(*configFlags[framesPerClipPtr])
|
fpc, err := strconv.Atoi(*configFlags[framesPerClipPtr])
|
||||||
|
@ -260,32 +293,16 @@ func main() {
|
||||||
config.Quantization = *configFlags[quantizationPtr]
|
config.Quantization = *configFlags[quantizationPtr]
|
||||||
config.Timeout = *configFlags[timeoutPtr]
|
config.Timeout = *configFlags[timeoutPtr]
|
||||||
config.IntraRefreshPeriod = *configFlags[intraRefreshPeriodPtr]
|
config.IntraRefreshPeriod = *configFlags[intraRefreshPeriodPtr]
|
||||||
|
|
||||||
if !*useNetsender {
|
|
||||||
// instantiate our own logger
|
|
||||||
config.Logger = smartlogger.New(loggerVerbosity, smartlogger.File, "/var/log/netsender/")
|
|
||||||
// run revid for the specified duration
|
|
||||||
startRevid(nil)
|
|
||||||
time.Sleep(*runDurationPtr)
|
|
||||||
stopRevid()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = run()
|
|
||||||
if err != nil {
|
|
||||||
config.Logger.Log(progName, "Error", err.Error()) // TODO(kortschak): Make this "Fatal" when that exists.
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize then run the main NetSender client
|
// initialize then run the main NetSender client
|
||||||
func run() error {
|
func run() error {
|
||||||
// initialize NetSender and use NetSender's logger
|
// initialize NetSender and use NetSender's logger
|
||||||
//config.Logger = netsender.Logger()
|
//config.Logger = netsender.Logger()
|
||||||
config.Logger.Log(progName, "Info", "Running in NetSender mode")
|
logger.Log(smartlogger.Info, pkg+"running in NetSender mode")
|
||||||
|
|
||||||
var ns netsender.Sender
|
var ns netsender.Sender
|
||||||
err := ns.Init(nil, nil, nil, nil)
|
err := ns.Init(logger, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -304,7 +321,7 @@ func run() error {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err := send(&ns); err != nil {
|
if err := send(&ns); err != nil {
|
||||||
config.Logger.Log(progName, "Warning", err.Error())
|
logger.Log(smartlogger.Error, pkg+"polling failed", "error", err.Error())
|
||||||
time.Sleep(netSendRetryTime)
|
time.Sleep(netSendRetryTime)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -313,14 +330,14 @@ func run() error {
|
||||||
// vars changed
|
// vars changed
|
||||||
vars, err := ns.Vars()
|
vars, err := ns.Vars()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
config.Logger.Log(progName, "Warning", err.Error())
|
logger.Log(smartlogger.Error, pkg+"netSender failed to get vars", "error", err.Error())
|
||||||
time.Sleep(netSendRetryTime)
|
time.Sleep(netSendRetryTime)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
vs = ns.VarSum()
|
vs = ns.VarSum()
|
||||||
if vars["mode"] == "Paused" {
|
if vars["mode"] == "Paused" {
|
||||||
if !paused {
|
if !paused {
|
||||||
config.Logger.Log(progName, "Info", "Pausing revid")
|
logger.Log(smartlogger.Info, pkg+"pausing revid")
|
||||||
stopRevid()
|
stopRevid()
|
||||||
paused = true
|
paused = true
|
||||||
}
|
}
|
||||||
|
@ -399,7 +416,7 @@ func updateRevid(ns *netsender.Sender, vars map[string]string, stop bool) error
|
||||||
case "FfmpegRtmp":
|
case "FfmpegRtmp":
|
||||||
config.Output = revid.FfmpegRtmp
|
config.Output = revid.FfmpegRtmp
|
||||||
default:
|
default:
|
||||||
rv.Log(revid.Warning, "Invalid Output param: "+value)
|
logger.Log(smartlogger.Warning, pkg+"invalid Output param", "value", value)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case "FramesPerClip":
|
case "FramesPerClip":
|
||||||
|
@ -407,7 +424,7 @@ func updateRevid(ns *netsender.Sender, vars map[string]string, stop bool) error
|
||||||
if fpc > 0 && err == nil {
|
if fpc > 0 && err == nil {
|
||||||
config.FramesPerClip = fpc
|
config.FramesPerClip = fpc
|
||||||
} else {
|
} else {
|
||||||
rv.Log(revid.Warning, "Invalid FramesPerClip param: "+value)
|
logger.Log(smartlogger.Warning, pkg+"invalid FramesPerClip param", "value", value)
|
||||||
}
|
}
|
||||||
case "RtmpUrl":
|
case "RtmpUrl":
|
||||||
config.RtmpUrl = value
|
config.RtmpUrl = value
|
||||||
|
@ -416,7 +433,7 @@ func updateRevid(ns *netsender.Sender, vars map[string]string, stop bool) error
|
||||||
if asInt > 0 && err == nil {
|
if asInt > 0 && err == nil {
|
||||||
config.Bitrate = value
|
config.Bitrate = value
|
||||||
} else {
|
} else {
|
||||||
rv.Log(revid.Warning, "Invalid Bitrate param: "+value)
|
logger.Log(smartlogger.Warning, pkg+"invalid Bitrate param", "value", value)
|
||||||
}
|
}
|
||||||
case "OutputFileName":
|
case "OutputFileName":
|
||||||
config.OutputFileName = value
|
config.OutputFileName = value
|
||||||
|
@ -427,21 +444,21 @@ func updateRevid(ns *netsender.Sender, vars map[string]string, stop bool) error
|
||||||
if asInt > 0 && err == nil {
|
if asInt > 0 && err == nil {
|
||||||
config.Height = value
|
config.Height = value
|
||||||
} else {
|
} else {
|
||||||
rv.Log(revid.Warning, "Invalid Height param: "+value)
|
logger.Log(smartlogger.Warning, pkg+"invalid Height param", "value", value)
|
||||||
}
|
}
|
||||||
case "Width":
|
case "Width":
|
||||||
asInt, err := strconv.Atoi(value)
|
asInt, err := strconv.Atoi(value)
|
||||||
if asInt > 0 && err == nil {
|
if asInt > 0 && err == nil {
|
||||||
config.Width = value
|
config.Width = value
|
||||||
} else {
|
} else {
|
||||||
rv.Log(revid.Warning, "Invalid Width param: "+value)
|
logger.Log(smartlogger.Warning, pkg+"invalid Width param", "value", value)
|
||||||
}
|
}
|
||||||
case "FrameRate":
|
case "FrameRate":
|
||||||
asInt, err := strconv.Atoi(value)
|
asInt, err := strconv.Atoi(value)
|
||||||
if asInt > 0 && err == nil {
|
if asInt > 0 && err == nil {
|
||||||
config.FrameRate = value
|
config.FrameRate = value
|
||||||
} else {
|
} else {
|
||||||
rv.Log(revid.Warning, "Invalid FrameRate param: "+value)
|
logger.Log(smartlogger.Warning, pkg+"invalid FrameRate param", "value", value)
|
||||||
}
|
}
|
||||||
case "HttpAddress":
|
case "HttpAddress":
|
||||||
config.HttpAddress = value
|
config.HttpAddress = value
|
||||||
|
@ -450,7 +467,7 @@ func updateRevid(ns *netsender.Sender, vars map[string]string, stop bool) error
|
||||||
if asInt > 0 && err == nil {
|
if asInt > 0 && err == nil {
|
||||||
config.Quantization = value
|
config.Quantization = value
|
||||||
} else {
|
} else {
|
||||||
rv.Log(revid.Warning, "Invalid Quantization param: "+value)
|
logger.Log(smartlogger.Warning, pkg+"invalid Quantization param", "value", value)
|
||||||
}
|
}
|
||||||
case "Timeout":
|
case "Timeout":
|
||||||
asInt, err := strconv.Atoi(value)
|
asInt, err := strconv.Atoi(value)
|
||||||
|
@ -469,7 +486,7 @@ func updateRevid(ns *netsender.Sender, vars map[string]string, stop bool) error
|
||||||
case "No":
|
case "No":
|
||||||
config.HorizontalFlip = revid.No
|
config.HorizontalFlip = revid.No
|
||||||
default:
|
default:
|
||||||
rv.Log(revid.Warning, "Invalid HorizontalFlip param: "+value)
|
logger.Log(smartlogger.Warning, pkg+"invalid HorizontalFlip param", "value", value)
|
||||||
}
|
}
|
||||||
case "VerticalFlip":
|
case "VerticalFlip":
|
||||||
switch value {
|
switch value {
|
||||||
|
@ -478,7 +495,7 @@ func updateRevid(ns *netsender.Sender, vars map[string]string, stop bool) error
|
||||||
case "No":
|
case "No":
|
||||||
config.VerticalFlip = revid.No
|
config.VerticalFlip = revid.No
|
||||||
default:
|
default:
|
||||||
rv.Log(revid.Warning, "Invalid VerticalFlip param: "+value)
|
logger.Log(smartlogger.Warning, pkg+"invalid VerticalFlip param", "value", value)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,12 +33,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/revid"
|
"bitbucket.org/ausocean/av/revid"
|
||||||
|
"bitbucket.org/ausocean/utils/smartlogger"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
inputFile = "../../../../test/test-data/av/input/betterInput.h264"
|
inputFile = "../../../../test/test-data/av/input/betterInput.h264"
|
||||||
frameRate = "25"
|
frameRate = "25"
|
||||||
runDuration = 120 * time.Second
|
runDuration = 120 * time.Second
|
||||||
|
logPath = "/var/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test h264 inputfile to flv format into rtmp using librtmp c wrapper
|
// Test h264 inputfile to flv format into rtmp using librtmp c wrapper
|
||||||
|
@ -59,10 +61,11 @@ func main() {
|
||||||
RtmpMethod: revid.LibRtmp,
|
RtmpMethod: revid.LibRtmp,
|
||||||
RtmpUrl: *rtmpUrlPtr,
|
RtmpUrl: *rtmpUrlPtr,
|
||||||
Packetization: revid.Flv,
|
Packetization: revid.Flv,
|
||||||
|
Logger: smartlogger.New(smartlogger.Info, logPath),
|
||||||
}
|
}
|
||||||
revidInst, err := revid.New(config, nil)
|
revidInst, err := revid.New(config, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Should not of have got an error!: %v\n", err.Error())
|
config.Logger.Log(smartlogger.Error, "Should not have got an error!: ", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
revidInst.Start()
|
revidInst.Start()
|
||||||
|
|
|
@ -28,10 +28,10 @@ LICENSE
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/revid"
|
"bitbucket.org/ausocean/av/revid"
|
||||||
|
"bitbucket.org/ausocean/utils/smartlogger"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -39,6 +39,7 @@ const (
|
||||||
outputFile = "output.ts"
|
outputFile = "output.ts"
|
||||||
frameRate = "25"
|
frameRate = "25"
|
||||||
runDuration = 120 * time.Second
|
runDuration = 120 * time.Second
|
||||||
|
logPath = "/var/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test h264 inputfile to flv format into rtmp using librtmp c wrapper
|
// Test h264 inputfile to flv format into rtmp using librtmp c wrapper
|
||||||
|
@ -51,10 +52,11 @@ func main() {
|
||||||
Output: revid.File,
|
Output: revid.File,
|
||||||
OutputFileName: outputFile,
|
OutputFileName: outputFile,
|
||||||
Packetization: revid.Mpegts,
|
Packetization: revid.Mpegts,
|
||||||
|
Logger: smartlogger.New(smartlogger.Info, logPath),
|
||||||
}
|
}
|
||||||
revidInst, err := revid.New(config, nil)
|
revidInst, err := revid.New(config, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Should not of have got an error!: %v\n", err.Error())
|
config.Logger.Log(smartlogger.Error, "Should not have got an error!:", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
revidInst.Start()
|
revidInst.Start()
|
||||||
|
|
117
revid/config.go
117
revid/config.go
|
@ -43,7 +43,7 @@ type Config struct {
|
||||||
RtmpMethod uint8
|
RtmpMethod uint8
|
||||||
Packetization uint8
|
Packetization uint8
|
||||||
QuantizationMode uint8
|
QuantizationMode uint8
|
||||||
Verbosity uint8
|
LogLevel int8
|
||||||
HorizontalFlip uint8
|
HorizontalFlip uint8
|
||||||
VerticalFlip uint8
|
VerticalFlip uint8
|
||||||
FramesPerClip int
|
FramesPerClip int
|
||||||
|
@ -58,7 +58,7 @@ type Config struct {
|
||||||
Quantization string
|
Quantization string
|
||||||
Timeout string
|
Timeout string
|
||||||
IntraRefreshPeriod string
|
IntraRefreshPeriod string
|
||||||
Logger smartlogger.LogInstance
|
Logger Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enums for config struct
|
// Enums for config struct
|
||||||
|
@ -99,39 +99,45 @@ const (
|
||||||
defaultFramesPerClip = 1
|
defaultFramesPerClip = 1
|
||||||
defaultVerticalFlip = No
|
defaultVerticalFlip = No
|
||||||
defaultHorizontalFlip = No
|
defaultHorizontalFlip = No
|
||||||
|
httpFramesPerClip = 7
|
||||||
|
defaultInputCodec = H264
|
||||||
|
defaultVerbosity = No
|
||||||
)
|
)
|
||||||
|
|
||||||
// Validate checks for any errors in the config fields and defaults settings
|
// Validate checks for any errors in the config fields and defaults settings
|
||||||
// if particular parameters have not been defined.
|
// if particular parameters have not been defined.
|
||||||
func (c *Config) Validate(r *Revid) error {
|
func (c *Config) Validate(r *Revid) error {
|
||||||
switch c.Verbosity {
|
switch c.LogLevel {
|
||||||
case Yes:
|
case Yes:
|
||||||
case No:
|
case No:
|
||||||
case NothingDefined:
|
case NothingDefined:
|
||||||
c.Verbosity = Yes
|
c.LogLevel = defaultVerbosity
|
||||||
r.Log(Warning, "No verbosity mode defined, defaulting to no Verbosity!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no LogLevel mode defined, defaulting",
|
||||||
|
"LogLevel", defaultVerbosity)
|
||||||
default:
|
default:
|
||||||
return errors.New("Bad Verbosity defined in config!")
|
return errors.New("bad LogLevel defined in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.QuantizationMode {
|
switch c.QuantizationMode {
|
||||||
case QuantizationOn:
|
case QuantizationOn:
|
||||||
case QuantizationOff:
|
case QuantizationOff:
|
||||||
case NothingDefined:
|
case NothingDefined:
|
||||||
r.Log(Warning, "No quantization mode defined, defaulting to QuantizationOff!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no quantization mode defined, defaulting",
|
||||||
|
"quantizationMode", QuantizationOff)
|
||||||
c.QuantizationMode = QuantizationOff
|
c.QuantizationMode = QuantizationOff
|
||||||
default:
|
default:
|
||||||
return errors.New("Bad QuantizationMode defined in config!")
|
return errors.New("bad QuantizationMode defined in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.Input {
|
switch c.Input {
|
||||||
case Raspivid:
|
case Raspivid:
|
||||||
case File:
|
case File:
|
||||||
case NothingDefined:
|
case NothingDefined:
|
||||||
r.Log(Warning, "No input type defined, defaulting to raspivid!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no input type defined, defaulting", "input",
|
||||||
|
defaultInput)
|
||||||
c.Input = defaultInput
|
c.Input = defaultInput
|
||||||
default:
|
default:
|
||||||
return errors.New("Bad input type defined in config!")
|
return errors.New("bad input type defined in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.InputCodec {
|
switch c.InputCodec {
|
||||||
|
@ -139,54 +145,59 @@ func (c *Config) Validate(r *Revid) error {
|
||||||
if c.Bitrate != "" && c.Quantization != "" {
|
if c.Bitrate != "" && c.Quantization != "" {
|
||||||
bitrate, err := strconv.Atoi(c.Bitrate)
|
bitrate, err := strconv.Atoi(c.Bitrate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Something is wrong with bitrate in conig!")
|
return errors.New("bitrate not an integer")
|
||||||
}
|
}
|
||||||
quantization, err := strconv.Atoi(c.Quantization)
|
quantization, err := strconv.Atoi(c.Quantization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Something is wrong with quantization in config!")
|
return errors.New("quantization not an integer")
|
||||||
}
|
}
|
||||||
if (bitrate > 0 && quantization > 0) || (bitrate == 0 && quantization == 0) {
|
if (bitrate > 0 && quantization > 0) || (bitrate == 0 && quantization == 0) {
|
||||||
return errors.New("Bad bitrate and quantization combination for H264 input!")
|
return errors.New("bad bitrate and quantization combination for H264 input")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Mjpeg:
|
case Mjpeg:
|
||||||
if c.Quantization != "" {
|
if c.Quantization != "" {
|
||||||
quantization, err := strconv.Atoi(c.Quantization)
|
quantization, err := strconv.Atoi(c.Quantization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Something is wrong with quantization in config!")
|
return errors.New("quantization not an integer")
|
||||||
}
|
}
|
||||||
if quantization > 0 || c.Bitrate == "" {
|
if quantization > 0 || c.Bitrate == "" {
|
||||||
return errors.New("Bad bitrate or quantization for mjpeg input!")
|
return errors.New("bad bitrate or quantization for mjpeg input")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case NothingDefined:
|
case NothingDefined:
|
||||||
r.Log(Warning, "No input codec defined, defaulting to h264!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no input codec defined, defaulting",
|
||||||
c.InputCodec = H264
|
"inputCodec", defaultInputCodec)
|
||||||
r.Log(Warning, "Defaulting bitrate to 0 and quantization to 35!")
|
c.InputCodec = defaultInputCodec
|
||||||
|
c.Logger.Log(smartlogger.Warning, pkg+"defaulting quantization", "quantization",
|
||||||
|
defaultQuantization)
|
||||||
c.Quantization = defaultQuantization
|
c.Quantization = defaultQuantization
|
||||||
default:
|
default:
|
||||||
return errors.New("Bad input codec defined in config!")
|
return errors.New("bad input codec defined in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.Output {
|
switch c.Output {
|
||||||
case File:
|
case File:
|
||||||
case Rtmp, FfmpegRtmp:
|
case Rtmp, FfmpegRtmp:
|
||||||
if c.RtmpUrl == "" {
|
if c.RtmpUrl == "" {
|
||||||
r.Log(Info, "No RTMP URL: falling back to HTTP")
|
c.Logger.Log(smartlogger.Info, pkg+"no RTMP URL: falling back to HTTP")
|
||||||
c.Output = Http
|
c.Output = Http
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
r.Log(Info, "Defaulting frames per clip to 1 for rtmp output!")
|
c.Logger.Log(smartlogger.Info, pkg+"defaulting frames per clip for rtmp out",
|
||||||
c.FramesPerClip = 1
|
"framesPerClip", defaultFramesPerClip)
|
||||||
|
c.FramesPerClip = defaultFramesPerClip
|
||||||
case NothingDefined:
|
case NothingDefined:
|
||||||
r.Log(Warning, "No output defined, defaulting to httpOut!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no output defined, defaulting", "output",
|
||||||
|
defaultOutput)
|
||||||
c.Output = defaultOutput
|
c.Output = defaultOutput
|
||||||
fallthrough
|
fallthrough
|
||||||
case Http:
|
case Http:
|
||||||
r.Log(Info, "Defaulting frames per clip to 7 for http output!")
|
c.Logger.Log(smartlogger.Info, pkg+"defaulting frames per clip for http out",
|
||||||
c.FramesPerClip = 7
|
"framesPerClip", httpFramesPerClip)
|
||||||
|
c.FramesPerClip = httpFramesPerClip
|
||||||
default:
|
default:
|
||||||
return errors.New("Bad output type defined in config!")
|
return errors.New("bad output type defined in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.Packetization {
|
switch c.Packetization {
|
||||||
|
@ -194,97 +205,107 @@ func (c *Config) Validate(r *Revid) error {
|
||||||
case Mpegts:
|
case Mpegts:
|
||||||
case Flv:
|
case Flv:
|
||||||
case NothingDefined:
|
case NothingDefined:
|
||||||
r.Log(Warning, "No packetization option defined, defaulting to none!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no packetization option defined, defaulting",
|
||||||
c.Packetization = Flv
|
"packetization", defaultPacketization)
|
||||||
|
c.Packetization = defaultPacketization
|
||||||
default:
|
default:
|
||||||
return errors.New("Bad packetization option defined in config!")
|
return errors.New("bad packetization option defined in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.HorizontalFlip {
|
switch c.HorizontalFlip {
|
||||||
case Yes:
|
case Yes:
|
||||||
case No:
|
case No:
|
||||||
case NothingDefined:
|
case NothingDefined:
|
||||||
r.Log(Warning, "No horizontal flip option defined, defaulting to not flipped!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no horizontal flip option defined, defaulting",
|
||||||
|
"horizontalFlip", defaultHorizontalFlip)
|
||||||
c.HorizontalFlip = defaultHorizontalFlip
|
c.HorizontalFlip = defaultHorizontalFlip
|
||||||
default:
|
default:
|
||||||
return errors.New("Bad horizontal flip option defined in config!")
|
return errors.New("bad horizontal flip option defined in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.VerticalFlip {
|
switch c.VerticalFlip {
|
||||||
case Yes:
|
case Yes:
|
||||||
case No:
|
case No:
|
||||||
case NothingDefined:
|
case NothingDefined:
|
||||||
r.Log(Warning, "No vertical flip option defined, defaulting to not flipped!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no vertical flip option defined, defaulting",
|
||||||
|
"verticalFlip", defaultVerticalFlip)
|
||||||
c.VerticalFlip = defaultVerticalFlip
|
c.VerticalFlip = defaultVerticalFlip
|
||||||
default:
|
default:
|
||||||
return errors.New("Bad vertical flip option defined in config!")
|
return errors.New("bad vertical flip option defined in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.FramesPerClip < 1 {
|
if c.FramesPerClip < 1 {
|
||||||
r.Log(Warning, "No FramesPerClip defined defined, defaulting to 1!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no FramesPerClip defined, defaulting",
|
||||||
|
"framesPerClip", defaultFramesPerClip)
|
||||||
c.FramesPerClip = defaultFramesPerClip
|
c.FramesPerClip = defaultFramesPerClip
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Width == "" {
|
if c.Width == "" {
|
||||||
r.Log(Warning, "No width defined, defaulting to 1280!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no width defined, defaulting", "width",
|
||||||
|
defaultWidth)
|
||||||
c.Width = defaultWidth
|
c.Width = defaultWidth
|
||||||
} else {
|
} else {
|
||||||
if integer, err := strconv.Atoi(c.Width); integer < 0 || err != nil {
|
if integer, err := strconv.Atoi(c.Width); integer < 0 || err != nil {
|
||||||
return errors.New("Bad width defined in config!")
|
return errors.New("width not unsigned integer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Height == "" {
|
if c.Height == "" {
|
||||||
r.Log(Warning, "No height defined, defaulting to 720!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no height defined, defaulting", "height",
|
||||||
|
defaultHeight)
|
||||||
c.Height = defaultHeight
|
c.Height = defaultHeight
|
||||||
} else {
|
} else {
|
||||||
if integer, err := strconv.Atoi(c.Height); integer < 0 || err != nil {
|
if integer, err := strconv.Atoi(c.Height); integer < 0 || err != nil {
|
||||||
return errors.New("Bad height defined in config!")
|
return errors.New("height not unsigned integer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.FrameRate == "" {
|
if c.FrameRate == "" {
|
||||||
r.Log(Warning, "No frame rate defined, defaulting to 25!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no frame rate defined, defaulting", "fps",
|
||||||
|
defaultFrameRate)
|
||||||
c.FrameRate = defaultFrameRate
|
c.FrameRate = defaultFrameRate
|
||||||
} else {
|
} else {
|
||||||
if integer, err := strconv.Atoi(c.FrameRate); integer < 0 || err != nil {
|
if integer, err := strconv.Atoi(c.FrameRate); integer < 0 || err != nil {
|
||||||
return errors.New("Bad frame rate defined in config!")
|
return errors.New("frame rate not unsigned integer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Bitrate == "" {
|
if c.Bitrate == "" {
|
||||||
r.Log(Warning, "No bitrate defined, defaulting!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no bitrate defined, defaulting", "bitrate",
|
||||||
|
defaultBitrate)
|
||||||
c.Bitrate = defaultBitrate
|
c.Bitrate = defaultBitrate
|
||||||
} else {
|
} else {
|
||||||
if integer, err := strconv.Atoi(c.Bitrate); integer < 0 || err != nil {
|
if integer, err := strconv.Atoi(c.Bitrate); integer < 0 || err != nil {
|
||||||
return errors.New("Bad bitrate defined in config!")
|
return errors.New("bitrate not unsigned integer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Timeout == "" {
|
if c.Timeout == "" {
|
||||||
r.Log(Warning, "No timeout defined, defaulting to 0!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no timeout defined, defaulting", "timeout", defaultTimeout)
|
||||||
c.Timeout = defaultTimeout
|
c.Timeout = defaultTimeout
|
||||||
} else {
|
} else {
|
||||||
if integer, err := strconv.Atoi(c.Timeout); integer < 0 || err != nil {
|
if integer, err := strconv.Atoi(c.Timeout); integer < 0 || err != nil {
|
||||||
return errors.New("Bad timeout defined in config!")
|
return errors.New("timeout not unsigned integer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.IntraRefreshPeriod == "" {
|
if c.IntraRefreshPeriod == "" {
|
||||||
r.Log(Warning, "No intra refresh defined, defaulting to 100!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no intra refresh defined, defaulting", "intraRefresh",
|
||||||
|
defaultIntraRefreshPeriod)
|
||||||
c.IntraRefreshPeriod = defaultIntraRefreshPeriod
|
c.IntraRefreshPeriod = defaultIntraRefreshPeriod
|
||||||
} else {
|
} else {
|
||||||
if integer, err := strconv.Atoi(c.IntraRefreshPeriod); integer < 0 || err != nil {
|
if integer, err := strconv.Atoi(c.IntraRefreshPeriod); integer < 0 || err != nil {
|
||||||
return errors.New("Bad intra refresh defined in config!")
|
return errors.New("intra refresh not unsigned integer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Quantization == "" {
|
if c.Quantization == "" {
|
||||||
r.Log(Warning, "No quantization defined, defaulting to 35!")
|
c.Logger.Log(smartlogger.Warning, pkg+"no quantization defined, defaulting", "quantization",
|
||||||
|
defaultQuantization)
|
||||||
c.Quantization = defaultQuantization
|
c.Quantization = defaultQuantization
|
||||||
} else {
|
} else {
|
||||||
if integer, err := strconv.Atoi(c.Quantization); integer < 0 || integer > 51 || err != nil {
|
if integer, err := strconv.Atoi(c.Quantization); integer < 0 || integer > 51 || err != nil {
|
||||||
return errors.New("Bad quantization defined in config!")
|
return errors.New("quantisation not unsigned integer or is over threshold")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
116
revid/revid.go
116
revid/revid.go
|
@ -31,11 +31,12 @@ package revid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
_ "fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/rtmp"
|
"bitbucket.org/ausocean/av/rtmp"
|
||||||
|
@ -45,6 +46,7 @@ import (
|
||||||
"bitbucket.org/ausocean/av/stream/mts"
|
"bitbucket.org/ausocean/av/stream/mts"
|
||||||
"bitbucket.org/ausocean/iot/pi/netsender"
|
"bitbucket.org/ausocean/iot/pi/netsender"
|
||||||
"bitbucket.org/ausocean/utils/ring"
|
"bitbucket.org/ausocean/utils/ring"
|
||||||
|
"bitbucket.org/ausocean/utils/smartlogger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Misc constants
|
// Misc constants
|
||||||
|
@ -71,6 +73,7 @@ const (
|
||||||
raspividNoOfTries = 3
|
raspividNoOfTries = 3
|
||||||
sendingWaitTime = 5 * time.Millisecond
|
sendingWaitTime = 5 * time.Millisecond
|
||||||
runContinuously = "0" // -t arg to raspivid
|
runContinuously = "0" // -t arg to raspivid
|
||||||
|
pkg = "revid:"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Log Types
|
// Log Types
|
||||||
|
@ -82,6 +85,11 @@ const (
|
||||||
Detail = "Detail"
|
Detail = "Detail"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Logger interface {
|
||||||
|
SetLevel(int8)
|
||||||
|
Log(level int8, message string, params ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
// Revid provides methods to control a revid session; providing methods
|
// Revid provides methods to control a revid session; providing methods
|
||||||
// to start, stop and change the state of an instance using the Config struct.
|
// to start, stop and change the state of an instance using the Config struct.
|
||||||
type Revid struct {
|
type Revid struct {
|
||||||
|
@ -139,14 +147,14 @@ func (r *Revid) reset(config Config) error {
|
||||||
r.config.Logger = config.Logger
|
r.config.Logger = config.Logger
|
||||||
err := config.Validate(r)
|
err := config.Validate(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Config struct is bad!: " + err.Error())
|
return errors.New("Config struct is bad: " + err.Error())
|
||||||
}
|
}
|
||||||
r.config = config
|
r.config = config
|
||||||
|
|
||||||
if r.destination != nil {
|
if r.destination != nil {
|
||||||
err = r.destination.close()
|
err = r.destination.close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Log(Error, err.Error())
|
r.config.Logger.Log(smartlogger.Error, pkg+"could not close destination", "error", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch r.config.Output {
|
switch r.config.Output {
|
||||||
|
@ -163,13 +171,13 @@ func (r *Revid) reset(config Config) error {
|
||||||
}
|
}
|
||||||
r.destination = s
|
r.destination = s
|
||||||
case Rtmp:
|
case Rtmp:
|
||||||
s, err := newRtmpSender(config.RtmpUrl, rtmpConnectionTimout, rtmpConnectionMaxTries, r.Log)
|
s, err := newRtmpSender(config.RtmpUrl, rtmpConnectionTimout, rtmpConnectionMaxTries, r.config.Logger.Log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
r.destination = s
|
r.destination = s
|
||||||
case Http:
|
case Http:
|
||||||
r.destination = newHttpSender(r.ns, r.Log)
|
r.destination = newHttpSender(r.ns, r.config.Logger.Log)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch r.config.Input {
|
switch r.config.Input {
|
||||||
|
@ -180,10 +188,10 @@ func (r *Revid) reset(config Config) error {
|
||||||
}
|
}
|
||||||
switch r.config.InputCodec {
|
switch r.config.InputCodec {
|
||||||
case H264:
|
case H264:
|
||||||
r.Log(Info, "using H264 lexer")
|
r.config.Logger.Log(smartlogger.Info, pkg+"using H264 lexer")
|
||||||
r.lexTo = lex.H264
|
r.lexTo = lex.H264
|
||||||
case Mjpeg:
|
case Mjpeg:
|
||||||
r.Log(Info, "using MJPEG lexer")
|
r.config.Logger.Log(smartlogger.Info, pkg+"using MJPEG lexer")
|
||||||
r.lexTo = lex.MJPEG
|
r.lexTo = lex.MJPEG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,11 +217,11 @@ func (r *Revid) reset(config Config) error {
|
||||||
r.getFrame = r.getFrameNoPacketization
|
r.getFrame = r.getFrameNoPacketization
|
||||||
return nil
|
return nil
|
||||||
case Mpegts:
|
case Mpegts:
|
||||||
r.Log(Info, "Using MPEGTS packetisation")
|
r.config.Logger.Log(smartlogger.Info, pkg+"using MPEGTS packetisation")
|
||||||
frameRate, _ := strconv.ParseFloat(r.config.FrameRate, 64)
|
frameRate, _ := strconv.ParseFloat(r.config.FrameRate, 64)
|
||||||
r.encoder = mts.NewEncoder(frameRate)
|
r.encoder = mts.NewEncoder(frameRate)
|
||||||
case Flv:
|
case Flv:
|
||||||
r.Log(Info, "Using FLV packetisation")
|
r.config.Logger.Log(smartlogger.Info, pkg+"using FLV packetisation")
|
||||||
frameRate, _ := strconv.Atoi(r.config.FrameRate)
|
frameRate, _ := strconv.Atoi(r.config.FrameRate)
|
||||||
r.encoder = flv.NewEncoder(true, true, frameRate)
|
r.encoder = flv.NewEncoder(true, true, frameRate)
|
||||||
}
|
}
|
||||||
|
@ -224,20 +232,6 @@ func (r *Revid) reset(config Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log takes a logtype and message and tries to send this information to the
|
|
||||||
// logger provided in the revid config - if there is one, otherwise the message
|
|
||||||
// is sent to stdout
|
|
||||||
func (r *Revid) Log(logType, m string) {
|
|
||||||
if r.config.Verbosity != Yes {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if r.config.Logger != nil {
|
|
||||||
r.config.Logger.Log("revid", logType, m)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(logType + ": " + m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRunning returns whether the receiver is running.
|
// IsRunning returns whether the receiver is running.
|
||||||
func (r *Revid) IsRunning() bool {
|
func (r *Revid) IsRunning() bool {
|
||||||
return r.isRunning
|
return r.isRunning
|
||||||
|
@ -247,31 +241,31 @@ func (r *Revid) IsRunning() bool {
|
||||||
// and packetising (if theres packetization) to a defined output.
|
// and packetising (if theres packetization) to a defined output.
|
||||||
func (r *Revid) Start() {
|
func (r *Revid) Start() {
|
||||||
if r.isRunning {
|
if r.isRunning {
|
||||||
r.Log(Warning, "Revid.Start() called but revid already running!")
|
r.config.Logger.Log(smartlogger.Warning, pkg+"revid.Start() called but revid already running")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Log(Info, "Starting Revid")
|
r.config.Logger.Log(smartlogger.Info, pkg+"starting Revid")
|
||||||
r.Log(Debug, "Setting up output")
|
r.config.Logger.Log(smartlogger.Debug, pkg+"setting up output")
|
||||||
r.isRunning = true
|
r.isRunning = true
|
||||||
r.Log(Info, "Starting output routine")
|
r.config.Logger.Log(smartlogger.Info, pkg+"starting output routine")
|
||||||
go r.outputClips()
|
go r.outputClips()
|
||||||
r.Log(Info, "Starting clip packing routine")
|
r.config.Logger.Log(smartlogger.Info, pkg+"starting clip packing routine")
|
||||||
go r.packClips()
|
go r.packClips()
|
||||||
r.Log(Info, "Setting up input and receiving content")
|
r.config.Logger.Log(smartlogger.Info, pkg+"setting up input and receiving content")
|
||||||
go r.setupInput()
|
go r.setupInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop halts any processing of video data from a camera or file
|
// Stop halts any processing of video data from a camera or file
|
||||||
func (r *Revid) Stop() {
|
func (r *Revid) Stop() {
|
||||||
if !r.isRunning {
|
if !r.isRunning {
|
||||||
r.Log(Warning, "Revid.Stop() called but revid not running!")
|
r.config.Logger.Log(smartlogger.Warning, pkg+"revid.Stop() called but revid not running")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Log(Info, "Stopping revid!")
|
r.config.Logger.Log(smartlogger.Info, pkg+"stopping revid")
|
||||||
r.isRunning = false
|
r.isRunning = false
|
||||||
|
|
||||||
r.Log(Info, "Killing input proccess!")
|
r.config.Logger.Log(smartlogger.Info, pkg+"killing input proccess")
|
||||||
// If a cmd process is running, we kill!
|
// If a cmd process is running, we kill!
|
||||||
if r.cmd != nil && r.cmd.Process != nil {
|
if r.cmd != nil && r.cmd.Process != nil {
|
||||||
r.cmd.Process.Kill()
|
r.cmd.Process.Kill()
|
||||||
|
@ -305,16 +299,17 @@ func (r *Revid) packClips() {
|
||||||
case frame := <-r.encoder.Stream():
|
case frame := <-r.encoder.Stream():
|
||||||
lenOfFrame := len(frame)
|
lenOfFrame := len(frame)
|
||||||
if lenOfFrame > ringBufferElementSize {
|
if lenOfFrame > ringBufferElementSize {
|
||||||
r.Log(Warning, fmt.Sprintf("Frame was too big: %v bytes, getting another one!", lenOfFrame))
|
r.config.Logger.Log(smartlogger.Warning, pkg+"frame was too big", "frame size", lenOfFrame)
|
||||||
frame = r.getFrame()
|
frame = r.getFrame()
|
||||||
lenOfFrame = len(frame)
|
lenOfFrame = len(frame)
|
||||||
}
|
}
|
||||||
_, err := r.ringBuffer.Write(frame)
|
_, err := r.ringBuffer.Write(frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == ring.ErrDropped {
|
if err == ring.ErrDropped {
|
||||||
r.Log(Warning, fmt.Sprintf("dropped %d byte frame", len(frame)))
|
r.config.Logger.Log(smartlogger.Warning, pkg+"dropped frame", "frame size", len(frame))
|
||||||
} else {
|
} else {
|
||||||
r.Log(Error, err.Error())
|
r.config.Logger.Log(smartlogger.Error, pkg+"unexpected ringbuffer write error",
|
||||||
|
"error", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packetCount++
|
packetCount++
|
||||||
|
@ -357,51 +352,51 @@ func (r *Revid) outputClips() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes += chunk.Len()
|
bytes += chunk.Len()
|
||||||
r.Log(Detail, "About to send")
|
r.config.Logger.Log(smartlogger.Debug, pkg+"about to send")
|
||||||
err = r.destination.load(chunk)
|
err = r.destination.load(chunk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Log(Error, "failed to load clip")
|
r.config.Logger.Log(smartlogger.Error, pkg+"failed to load clip")
|
||||||
}
|
}
|
||||||
err = r.destination.send()
|
err = r.destination.send()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
r.Log(Detail, "sent clip")
|
r.config.Logger.Log(smartlogger.Debug, pkg+"sent clip")
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.isRunning && err != nil && chunk.Len() > 11 {
|
if r.isRunning && err != nil && chunk.Len() > 11 {
|
||||||
r.Log(Debug, "Send failed! Trying again")
|
r.config.Logger.Log(smartlogger.Debug, pkg+"send failed, trying again")
|
||||||
// Try and send again
|
// Try and send again
|
||||||
err = r.destination.send()
|
err = r.destination.send()
|
||||||
r.Log(Error, err.Error())
|
r.config.Logger.Log(smartlogger.Error, pkg+"destination send error", "error", err.Error())
|
||||||
|
|
||||||
// if there's still an error we try and reconnect, unless we're stopping
|
// if there's still an error we try and reconnect, unless we're stopping
|
||||||
for r.isRunning && err != nil {
|
for r.isRunning && err != nil {
|
||||||
r.Log(Debug, "Send failed a again! Trying to reconnect...")
|
r.config.Logger.Log(smartlogger.Debug, pkg+"send failed a again, trying to reconnect...")
|
||||||
time.Sleep(time.Duration(sendFailedDelay) * time.Millisecond)
|
time.Sleep(time.Duration(sendFailedDelay) * time.Millisecond)
|
||||||
r.Log(Error, err.Error())
|
r.config.Logger.Log(smartlogger.Error, pkg+"send failed with error", "error", err.Error())
|
||||||
|
|
||||||
if rs, ok := r.destination.(restarter); ok {
|
if rs, ok := r.destination.(restarter); ok {
|
||||||
r.Log(Debug, fmt.Sprintf("restarting %T session", rs))
|
r.config.Logger.Log(smartlogger.Debug, pkg+"restarting session", "session", rs)
|
||||||
err = rs.restart()
|
err = rs.restart()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(kortschak): Make this "Fatal" when that exists.
|
// TODO(kortschak): Make this "Fatal" when that exists.
|
||||||
r.Log(Error, "failed to restart rtmp session")
|
r.config.Logger.Log(smartlogger.Error, pkg+"failed to restart rtmp session", "error", err.Error())
|
||||||
r.isRunning = false
|
r.isRunning = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Log(Info, "restarted rtmp session")
|
r.config.Logger.Log(smartlogger.Info, pkg+"restarted rtmp session")
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Log(Debug, "Trying to send again with new connection...")
|
r.config.Logger.Log(smartlogger.Debug, pkg+"trying to send again with new connection")
|
||||||
err = r.destination.send()
|
err = r.destination.send()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Log(Error, err.Error())
|
r.config.Logger.Log(smartlogger.Error, pkg+"send failed with error", "error", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.destination.release()
|
r.destination.release()
|
||||||
|
|
||||||
r.Log(Detail, "Done reading that clip from ringbuffer...")
|
r.config.Logger.Log(smartlogger.Debug, pkg+"done reading that clip from ringbuffer")
|
||||||
|
|
||||||
// Log some information regarding bitrate and ring buffer size if it's time
|
// Log some information regarding bitrate and ring buffer size if it's time
|
||||||
now = time.Now()
|
now = time.Now()
|
||||||
|
@ -409,23 +404,23 @@ func (r *Revid) outputClips() {
|
||||||
if deltaTime > bitrateTime {
|
if deltaTime > bitrateTime {
|
||||||
// FIXME(kortschak): For subsecond deltaTime, this will give infinite bitrate.
|
// FIXME(kortschak): For subsecond deltaTime, this will give infinite bitrate.
|
||||||
r.bitrate = int(float64(bytes*8) / float64(deltaTime/time.Second))
|
r.bitrate = int(float64(bytes*8) / float64(deltaTime/time.Second))
|
||||||
r.Log(Debug, fmt.Sprintf("Bitrate: %v bits/s\n", r.bitrate))
|
r.config.Logger.Log(smartlogger.Debug, pkg+"bitrate (bits/s)", "bitrate", r.bitrate)
|
||||||
r.Log(Debug, fmt.Sprintf("Ring buffer size: %v\n", r.ringBuffer.Len()))
|
r.config.Logger.Log(smartlogger.Debug, pkg+"ring buffer size", "value", r.ringBuffer.Len())
|
||||||
prevTime = now
|
prevTime = now
|
||||||
bytes = 0
|
bytes = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.Log(Info, "Not outputting clips anymore!")
|
r.config.Logger.Log(smartlogger.Info, pkg+"not outputting clips anymore")
|
||||||
err := r.destination.close()
|
err := r.destination.close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Log(Error, "failed to close destination")
|
r.config.Logger.Log(smartlogger.Error, pkg+"failed to close destination", "error", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// startRaspivid sets up things for input from raspivid i.e. starts
|
// startRaspivid sets up things for input from raspivid i.e. starts
|
||||||
// a raspivid process and pipes it's data output.
|
// a raspivid process and pipes it's data output.
|
||||||
func (r *Revid) startRaspivid() error {
|
func (r *Revid) startRaspivid() error {
|
||||||
r.Log(Info, "Starting raspivid!")
|
r.config.Logger.Log(smartlogger.Info, pkg+"starting raspivid")
|
||||||
switch r.config.InputCodec {
|
switch r.config.InputCodec {
|
||||||
case H264:
|
case H264:
|
||||||
args := []string{
|
args := []string{
|
||||||
|
@ -440,6 +435,7 @@ func (r *Revid) startRaspivid() error {
|
||||||
"-ih",
|
"-ih",
|
||||||
"-g", r.config.IntraRefreshPeriod,
|
"-g", r.config.IntraRefreshPeriod,
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.config.QuantizationMode == QuantizationOn {
|
if r.config.QuantizationMode == QuantizationOn {
|
||||||
args = append(args, "-qp", r.config.Quantization)
|
args = append(args, "-qp", r.config.Quantization)
|
||||||
}
|
}
|
||||||
|
@ -449,11 +445,13 @@ func (r *Revid) startRaspivid() error {
|
||||||
if r.config.VerticalFlip == Yes {
|
if r.config.VerticalFlip == Yes {
|
||||||
args = append(args, "-vf")
|
args = append(args, "-vf")
|
||||||
}
|
}
|
||||||
|
// Log all the args and create []string
|
||||||
r.Log(Info, fmt.Sprintf("Starting raspivid with args: %v", args))
|
argsStr := strings.Join(args, " ")
|
||||||
r.cmd = exec.Command("raspivid", args...)
|
r.config.Logger.Log(smartlogger.Info, pkg+"raspivid args", "raspividArgs", argsStr)
|
||||||
|
r.cmd = exec.Command("raspivid", argsStr)
|
||||||
|
|
||||||
case Mjpeg:
|
case Mjpeg:
|
||||||
|
// FIXME(saxon): do above in this case too
|
||||||
r.cmd = exec.Command("raspivid",
|
r.cmd = exec.Command("raspivid",
|
||||||
"-cd", "MJPEG",
|
"-cd", "MJPEG",
|
||||||
"-o", "-",
|
"-o", "-",
|
||||||
|
@ -472,9 +470,9 @@ func (r *Revid) startRaspivid() error {
|
||||||
}
|
}
|
||||||
r.inputReader = stdout
|
r.inputReader = stdout
|
||||||
go func() {
|
go func() {
|
||||||
r.Log(Info, "Reading camera data!")
|
r.config.Logger.Log(smartlogger.Info, pkg+"reading camera data")
|
||||||
r.lexTo(r.encoder, r.inputReader, 0)
|
r.lexTo(r.encoder, r.inputReader, 0)
|
||||||
r.Log(Info, "Not trying to read from camera anymore!")
|
r.config.Logger.Log(smartlogger.Info, pkg+"not trying to read from camera anymore")
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -489,7 +487,7 @@ func (r *Revid) setupInputForFile() error {
|
||||||
|
|
||||||
f, err := os.Open(r.config.InputFileName)
|
f, err := os.Open(r.config.InputFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Log(Error, err.Error())
|
r.config.Logger.Log(smartlogger.Error, err.Error())
|
||||||
r.Stop()
|
r.Stop()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"bitbucket.org/ausocean/av/rtmp"
|
"bitbucket.org/ausocean/av/rtmp"
|
||||||
"bitbucket.org/ausocean/iot/pi/netsender"
|
"bitbucket.org/ausocean/iot/pi/netsender"
|
||||||
"bitbucket.org/ausocean/utils/ring"
|
"bitbucket.org/ausocean/utils/ring"
|
||||||
|
"bitbucket.org/ausocean/utils/smartlogger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// loadSender is a destination to send a *ring.Chunk to.
|
// loadSender is a destination to send a *ring.Chunk to.
|
||||||
|
@ -102,12 +103,12 @@ func (s *fileSender) close() error {
|
||||||
type httpSender struct {
|
type httpSender struct {
|
||||||
client *netsender.Sender
|
client *netsender.Sender
|
||||||
|
|
||||||
log func(lvl, msg string)
|
log func(lvl int8, msg string, args ...interface{})
|
||||||
|
|
||||||
chunk *ring.Chunk
|
chunk *ring.Chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHttpSender(ns *netsender.Sender, log func(lvl, msg string)) *httpSender {
|
func newHttpSender(ns *netsender.Sender, log func(lvl int8, msg string, args ...interface{})) *httpSender {
|
||||||
return &httpSender{
|
return &httpSender{
|
||||||
client: ns,
|
client: ns,
|
||||||
log: log,
|
log: log,
|
||||||
|
@ -214,14 +215,14 @@ type rtmpSender struct {
|
||||||
url string
|
url string
|
||||||
timeout uint
|
timeout uint
|
||||||
retries int
|
retries int
|
||||||
log func(lvl, msg string)
|
log func(lvl int8, msg string, args ...interface{})
|
||||||
|
|
||||||
chunk *ring.Chunk
|
chunk *ring.Chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ restarter = (*rtmpSender)(nil)
|
var _ restarter = (*rtmpSender)(nil)
|
||||||
|
|
||||||
func newRtmpSender(url string, timeout uint, retries int, log func(lvl, msg string)) (*rtmpSender, error) {
|
func newRtmpSender(url string, timeout uint, retries int, log func(lvl int8, msg string, args ...interface{})) (*rtmpSender, error) {
|
||||||
var sess *rtmp.Session
|
var sess *rtmp.Session
|
||||||
var err error
|
var err error
|
||||||
for n := 0; n < retries; n++ {
|
for n := 0; n < retries; n++ {
|
||||||
|
@ -230,10 +231,10 @@ func newRtmpSender(url string, timeout uint, retries int, log func(lvl, msg stri
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
log(Error, err.Error())
|
log(smartlogger.Error, err.Error())
|
||||||
sess.Close()
|
sess.Close()
|
||||||
if n < retries-1 {
|
if n < retries-1 {
|
||||||
log(Info, "retry rtmp connection")
|
log(smartlogger.Info, pkg+"retry rtmp connection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -276,10 +277,10 @@ func (s *rtmpSender) restart() error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
s.log(Error, err.Error())
|
s.log(smartlogger.Error, err.Error())
|
||||||
s.sess.Close()
|
s.sess.Close()
|
||||||
if n < s.retries-1 {
|
if n < s.retries-1 {
|
||||||
s.log(Info, "retry rtmp connection")
|
s.log(smartlogger.Info, pkg+"retry rtmp connection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in New Issue