mirror of https://bitbucket.org/ausocean/av.git
505 lines
14 KiB
Go
505 lines
14 KiB
Go
/*
|
|
NAME
|
|
RevidCLI.go
|
|
|
|
DESCRIPTION
|
|
See Readme.md
|
|
|
|
AUTHORS
|
|
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
|
Jack Richardson <jack@ausocean.org>
|
|
|
|
LICENSE
|
|
RevidCLI.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean)
|
|
|
|
It is free software: you can redistribute it and/or modify them
|
|
under the terms of the GNU General Public License as published by the
|
|
Free Software Foundation, either version 3 of the License, or (at your
|
|
option) any later version.
|
|
|
|
It is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with revid in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses).
|
|
*/
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"errors"
|
|
"os/exec"
|
|
"flag"
|
|
"strconv"
|
|
"time"
|
|
|
|
"bitbucket.org/ausocean/av/revid"
|
|
_"bitbucket.org/ausocean/utils/smartLogger"
|
|
"bitbucket.org/ausocean/iot/pi/netsender"
|
|
|
|
linuxproc "github.com/c9s/goprocinfo/linux"
|
|
)
|
|
|
|
// Indexes for configFlags
|
|
const (
|
|
inputPtr = 0
|
|
inputCodecPtr = 1
|
|
outputPtr = 2
|
|
rtmpMethodPtr = 3
|
|
packetizationPtr = 4
|
|
quantizationModePtr = 5
|
|
verbosityPtr = 6
|
|
framesPerClipPtr = 7
|
|
rtmpUrlPtr = 8
|
|
bitratePtr = 9
|
|
outputFileNamePtr = 10
|
|
inputFileNamePtr = 11
|
|
heightPtr = 12
|
|
widthPtr = 13
|
|
frameRatePtr = 14
|
|
httpAddressPtr = 15
|
|
quantizationPtr = 16
|
|
timeoutPtr = 17
|
|
intraRefreshPeriodPtr = 18
|
|
verticalFlipPtr = 19
|
|
horizontalFlipPtr = 20
|
|
)
|
|
|
|
// Other misc consts
|
|
const (
|
|
sleepTime = 2 * 43200
|
|
defaultRunDuration = 2 * 43200
|
|
noOfConfigFlags = 21
|
|
revidStopTime = 5
|
|
prepTime = 20
|
|
loggerVerbosity = 3
|
|
)
|
|
|
|
const (
|
|
lightRelayPin = 19
|
|
ds18b20Temp = 60
|
|
dht11Temp = 40
|
|
dht11Hum = 41
|
|
dht22Temp = 50
|
|
dht22Hum = 51
|
|
dhtPin = 22
|
|
cpuTemp = 21
|
|
cpuUsage = 20
|
|
priority = 25
|
|
)
|
|
|
|
// Globals
|
|
var (
|
|
configReceived bool = true
|
|
varSum int = 0
|
|
vars = make(map[string]string)
|
|
revidInst revid.Revid
|
|
config revid.Config
|
|
)
|
|
|
|
func main() {
|
|
flagNames := [noOfConfigFlags][2]string{
|
|
{"Input", "The input type"},
|
|
{"InputCodec", "The codec of the input"},
|
|
{"Output", "The output type"},
|
|
{"RtmpMethod", "The method used to send over rtmp (ffmpeg or librtmp)"},
|
|
{"Packetization", "The method of data packetisation"},
|
|
{"QuantizationMode", "The level of quantization"},
|
|
{"Verbosity", "Verbosity on or off"},
|
|
{"FramesPerClip", "Number of frames per clip sent"},
|
|
{"RtmpUrl", "Url of rtmp endpoint"},
|
|
{"Bitrate", "Bitrate of recorded video"},
|
|
{"OutputFileName", "The directory of the output file"},
|
|
{"InputFileName", "The directory of the input file"},
|
|
{"Height", "Height in pixels"},
|
|
{"Width", "Width in pixels"},
|
|
{"FrameRate", "Frame rate of captured video"},
|
|
{"HttpAddress", "Destination address of http posts"},
|
|
{"Quantization", "Desired quantization value"},
|
|
{"Timeout", "Http timeout in seconds"},
|
|
{"IntraRefreshPeriod", "The IntraRefreshPeriod i.e. how many keyframes we send"},
|
|
{"VerticalFlip", "Flip video vertically"},
|
|
{"HorizontalFlip", "Flip video horizontally"},
|
|
}
|
|
|
|
// Create the configFlags based on the flagNames array
|
|
configFlags := make([](*string),noOfConfigFlags)
|
|
for i := 0; i < noOfConfigFlags; i++ {
|
|
configFlags[i] = flag.String(flagNames[i][0], "", flagNames[i][1])
|
|
}
|
|
|
|
// Do we want a netsender session
|
|
netSenderFlagPtr := flag.Bool("NetSender", false, "Are we checking vars through netsender?")
|
|
// User might also want to define how long revid runs for
|
|
runDurationPtr := flag.Int("runDuration",defaultRunDuration,"How long do you want revid to run for?")
|
|
|
|
flag.Parse()
|
|
|
|
switch *configFlags[inputPtr] {
|
|
case "Raspivid":
|
|
config.Input = revid.Raspivid
|
|
case "Rtp":
|
|
config.Input = revid.Rtp
|
|
case "File":
|
|
config.Input = revid.File
|
|
case "":
|
|
default:
|
|
fmt.Println("Bad input argument!")
|
|
}
|
|
|
|
switch *configFlags[inputCodecPtr] {
|
|
case "H264Codec":
|
|
config.InputCodec = revid.H264Codec
|
|
case "":
|
|
default:
|
|
fmt.Println("Bad input codec argument!")
|
|
}
|
|
|
|
switch *configFlags[outputPtr] {
|
|
case "File":
|
|
config.Output = revid.File
|
|
case "Http":
|
|
config.Output = revid.Http
|
|
case "NativeRtmp":
|
|
config.Output = revid.NativeRtmp
|
|
case "FfmpegRtmp":
|
|
config.Output = revid.FfmpegRtmp
|
|
case "":
|
|
default:
|
|
fmt.Println("Bad output argument!")
|
|
}
|
|
|
|
switch *configFlags[rtmpMethodPtr] {
|
|
case "Ffmpeg":
|
|
config.RtmpMethod = revid.Ffmpeg
|
|
case "LibRtmp":
|
|
config.RtmpMethod = revid.LibRtmp
|
|
case "":
|
|
default:
|
|
fmt.Println("Bad rtmp method argument!")
|
|
}
|
|
|
|
switch *configFlags[packetizationPtr] {
|
|
case "None":
|
|
config.Packetization = revid.None
|
|
case "Rtp":
|
|
config.Packetization = revid.Rtp
|
|
case "Flv":
|
|
config.Packetization = revid.Flv
|
|
case "":
|
|
default:
|
|
fmt.Println("Bad packetization argument!")
|
|
}
|
|
|
|
switch *configFlags[quantizationModePtr] {
|
|
case "QuantizationOn":
|
|
config.QuantizationMode = revid.QuantizationOn
|
|
case "QuantizationOff":
|
|
config.QuantizationMode = revid.QuantizationOff
|
|
case "":
|
|
default:
|
|
fmt.Println("Bad quantization mode argument!")
|
|
}
|
|
|
|
switch *configFlags[verbosityPtr] {
|
|
case "No":
|
|
config.Verbosity = revid.No
|
|
case "Yes":
|
|
config.Verbosity = revid.Yes
|
|
case "":
|
|
default:
|
|
fmt.Println("Bad verbosity argument!")
|
|
}
|
|
|
|
switch *configFlags[horizontalFlipPtr] {
|
|
case "No":
|
|
config.HorizontalFlip = revid.No
|
|
case "Yes":
|
|
config.HorizontalFlip = revid.Yes
|
|
case "":
|
|
config.HorizontalFlip = revid.No
|
|
default:
|
|
fmt.Println("Bad horizontal flip option!")
|
|
}
|
|
|
|
switch *configFlags[verticalFlipPtr] {
|
|
case "No":
|
|
config.VerticalFlip = revid.No
|
|
case "Yes":
|
|
config.VerticalFlip = revid.Yes
|
|
case "":
|
|
config.VerticalFlip = revid.No
|
|
default:
|
|
fmt.Println("Bad vertical flip option!")
|
|
}
|
|
|
|
config.FramesPerClip = *configFlags[framesPerClipPtr]
|
|
config.RtmpUrl = *configFlags[rtmpUrlPtr]
|
|
config.Bitrate = *configFlags[bitratePtr]
|
|
config.OutputFileName = *configFlags[outputFileNamePtr]
|
|
config.InputFileName = *configFlags[inputFileNamePtr]
|
|
config.Height = *configFlags[heightPtr]
|
|
config.Width = *configFlags[widthPtr]
|
|
config.FrameRate = *configFlags[frameRatePtr]
|
|
config.HttpAddress = *configFlags[httpAddressPtr]
|
|
config.Quantization = *configFlags[quantizationPtr]
|
|
config.Timeout = *configFlags[timeoutPtr]
|
|
config.IntraRefreshPeriod = *configFlags[intraRefreshPeriodPtr]
|
|
|
|
// Also give the config a logger object
|
|
//config.Logger = smartLogger.New(loggerVerbosity, smartLogger.White)
|
|
|
|
|
|
|
|
//init netsender
|
|
if *netSenderFlagPtr{
|
|
netsenderInit()
|
|
}
|
|
|
|
time.Sleep(time.Duration(prepTime) * time.Second)
|
|
createRevidInstance()
|
|
revidInst.Start()
|
|
|
|
// Is the netsender flag been used ? if so run this loop
|
|
for *netSenderFlagPtr {
|
|
periodicNetsenderReport()
|
|
sleepTime, _ := strconv.Atoi(netsender.GetConfigParam("monPeriod"))
|
|
time.Sleep(time.Duration(sleepTime) * time.Second)
|
|
}
|
|
|
|
// If we're not running a netsender session then we run revid for the amount
|
|
// of time the user defined or the default 2 days
|
|
time.Sleep( time.Duration(*runDurationPtr) * time.Second )
|
|
revidInst.Stop()
|
|
}
|
|
|
|
func netsenderInit() {
|
|
//initialize netsender and assign function to check X pins
|
|
config.Logger = netsender.GetLogger()
|
|
netsender.Init(false)
|
|
netsender.ExternalReader = revidReportActions
|
|
|
|
//try get initial config
|
|
if err := netsender.GetConfig(); err != nil {
|
|
//default to a config?????
|
|
config.Logger.Log("Error", err.Error())
|
|
} else {
|
|
configReceived = true;
|
|
}
|
|
}
|
|
|
|
//periodicNetsenderReport is called by the main function every monPeriod seconds. It makes sure a config has been recieved, and then
|
|
//reports back CPU stats and updates vars
|
|
func periodicNetsenderReport(){
|
|
if !configReceived {
|
|
if err := netsender.GetConfig(); err != nil {
|
|
config.Logger.Log("Error", err.Error())
|
|
} else {
|
|
configReceived = true;
|
|
}
|
|
}
|
|
|
|
//inputs := []string{"X20", "X21"}
|
|
inputs := netsender.SplitCSV(netsender.GetConfigParam("inputs"))
|
|
if _, _, err := netsender.Send(netsender.RequestPoll, inputs); err != nil {
|
|
config.Logger.Log("Error", err.Error())
|
|
}
|
|
|
|
if cloudVarSum := netsender.GetVarSum(); cloudVarSum != varSum {
|
|
if newVars, err := netsender.GetVars(); err != nil {
|
|
config.Logger.Log("Error", err.Error())
|
|
} else {
|
|
varSum = cloudVarSum
|
|
for newVar, value := range newVars {
|
|
if currentValue, varExists := vars[newVar]; !varExists || currentValue != value {
|
|
vars[newVar] = value
|
|
}
|
|
}
|
|
}
|
|
updateRevid()
|
|
}
|
|
}
|
|
|
|
|
|
func updateRevid() {
|
|
revidInst.Stop()
|
|
time.Sleep( time.Duration(revidStopTime) * time.Second )
|
|
//look through var map and update revid where needed
|
|
|
|
for key, value := range vars {
|
|
switch key {
|
|
case "FramesPerClip":
|
|
asInt,err := strconv.Atoi(value)
|
|
if asInt > 0 && err == nil {
|
|
config.FramesPerClip = value
|
|
}
|
|
case "RtmpUrl":
|
|
config.RtmpUrl = value
|
|
case "Bitrate":
|
|
asInt,err := strconv.Atoi(value)
|
|
if asInt > 0 && err == nil {
|
|
config.Bitrate = value
|
|
}
|
|
case "OutputFileName":
|
|
config.OutputFileName = value
|
|
case "InputFileName":
|
|
config.InputFileName = value
|
|
case "Height":
|
|
asInt,err := strconv.Atoi(value)
|
|
if asInt > 0 && err == nil {
|
|
config.Height = value
|
|
}
|
|
case "Width":
|
|
asInt,err := strconv.Atoi(value)
|
|
if asInt > 0 && err == nil {
|
|
config.Width = value
|
|
}
|
|
case "FrameRate":
|
|
asInt,err := strconv.Atoi(value)
|
|
if asInt > 0 && err == nil {
|
|
config.FrameRate = value
|
|
}
|
|
case "HttpAddress":
|
|
config.HttpAddress = value
|
|
case "Quantization":
|
|
asInt,err := strconv.Atoi(value)
|
|
if asInt > 0 && err == nil {
|
|
config.Quantization = value
|
|
}
|
|
case "Timeout":
|
|
asInt,err := strconv.Atoi(value)
|
|
if asInt > 0 && err == nil {
|
|
config.Timeout = value
|
|
}
|
|
case "IntraRefreshPeriod":
|
|
asInt,err := strconv.Atoi(value)
|
|
if asInt > 0 && err == nil {
|
|
config.IntraRefreshPeriod = value
|
|
}
|
|
case "HorizontalFlip":
|
|
switch value {
|
|
case "Yes":
|
|
config.HorizontalFlip = revid.Yes
|
|
case "No":
|
|
config.HorizontalFlip = revid.No
|
|
}
|
|
case "VerticalFlip":
|
|
switch value {
|
|
case "Yes":
|
|
config.VerticalFlip = revid.Yes
|
|
case "No":
|
|
config.VerticalFlip = revid.No
|
|
}
|
|
}
|
|
}
|
|
|
|
createRevidInstance()
|
|
revidInst.Start()
|
|
}
|
|
|
|
func revidReportActions(pin int) (int, error) {
|
|
switch {
|
|
// case pin == ds18b20Temp:
|
|
// sensors, err := ds18b20.Sensors()
|
|
// if err != nil {
|
|
// processManager.Logger.Log("Error", "Error retrieving connected sensors: "+err.Error())
|
|
// return -1, errors.New("No sensors connected")
|
|
// }
|
|
|
|
// t, err := ds18b20.Temperature(sensors[0])
|
|
// if err == nil {
|
|
// return int(t), nil
|
|
// }
|
|
|
|
// return -1, errors.New("Unable to read temperature")
|
|
|
|
// case pin == dht11Temp:
|
|
// if val, _, _, err := dht.ReadDHTxxWithRetry(dht.DHT11, dhtPin, false, 5); err != nil {
|
|
// return -1, errors.New("DHT Read Err: " + err.Error())
|
|
// } else {
|
|
// return int(val), nil
|
|
// }
|
|
|
|
// case pin == dht11Hum:
|
|
// if _, val, _, err := dht.ReadDHTxxWithRetry(dht.DHT11, dhtPin, false, 5); err != nil {
|
|
// return -1, errors.New("DHT Read Err: " + err.Error())
|
|
// } else {
|
|
// return int(val), nil
|
|
// }
|
|
|
|
// case pin == dht22Temp:
|
|
// if val, _, _, err := dht.ReadDHTxxWithRetry(dht.DHT22, dhtPin, false, 5); err != nil {
|
|
// return -1, errors.New("DHT Read Err: " + err.Error())
|
|
// } else {
|
|
// return int(val), nil
|
|
// }
|
|
|
|
// case pin == dht22Hum:
|
|
// if _, val, _, err := dht.ReadDHTxxWithRetry(dht.DHT22, dhtPin, false, 5); err != nil {
|
|
// return -1, errors.New("DHT Read Err: " + err.Error())
|
|
// } else {
|
|
// return int(val), nil
|
|
// }
|
|
|
|
//function to measure temp of cpu
|
|
case pin == cpuTemp:
|
|
var out []byte
|
|
var err error
|
|
var val float64
|
|
if out, err = exec.Command("/opt/vc/bin/vcgencmd", "measure_temp").Output(); err != nil {
|
|
return -1, errors.New("CPU Temp Read Err: " + err.Error())
|
|
}
|
|
if val, err = strconv.ParseFloat(string(out[5:len(out)-3]), 32); err != nil {
|
|
return -1, errors.New("CPU Temp Read Err: " + err.Error())
|
|
}
|
|
return int(val), nil
|
|
//function to measure usage of cpu
|
|
case pin == cpuUsage:
|
|
stat, err := linuxproc.ReadStat("/proc/stat")
|
|
if err != nil {
|
|
return -1, errors.New("CPU Uage Read Err: " + err.Error())
|
|
}
|
|
total1 := stat.CPUStatAll.User + stat.CPUStatAll.Nice + stat.CPUStatAll.System +
|
|
stat.CPUStatAll.Idle + stat.CPUStatAll.IOWait + stat.CPUStatAll.IRQ +
|
|
stat.CPUStatAll.SoftIRQ + stat.CPUStatAll.Steal + stat.CPUStatAll.Guest +
|
|
stat.CPUStatAll.GuestNice
|
|
idle1 := stat.CPUStatAll.Idle
|
|
|
|
time.Sleep(time.Millisecond * 1000)
|
|
|
|
stat, err = linuxproc.ReadStat("/proc/stat")
|
|
if err != nil {
|
|
return -1, errors.New("CPU Usage Read Err: " + err.Error())
|
|
}
|
|
total2 := stat.CPUStatAll.User + stat.CPUStatAll.Nice + stat.CPUStatAll.System +
|
|
stat.CPUStatAll.Idle + stat.CPUStatAll.IOWait + stat.CPUStatAll.IRQ +
|
|
stat.CPUStatAll.SoftIRQ + stat.CPUStatAll.Steal + stat.CPUStatAll.Guest +
|
|
stat.CPUStatAll.GuestNice
|
|
idle2 := stat.CPUStatAll.Idle
|
|
|
|
return int((1.0 - (float64(idle2-idle1) / float64(total2-total1))) * 100), nil
|
|
|
|
// case pin == priority:
|
|
// return int(processManager.PriorityLevel), nil
|
|
|
|
default:
|
|
return -1, errors.New("External pin" + strconv.Itoa(pin) + " not defined")
|
|
}
|
|
}
|
|
|
|
func createRevidInstance(){
|
|
// Try to create the revid instance with the given config
|
|
var err error
|
|
for revidInst, err = revid.NewRevid(config); err != nil; {
|
|
// If the config does have a logger, use it to output error, otherwise
|
|
// just output to std output
|
|
if config.Logger != nil {
|
|
config.Logger.Log("FATAL ERROR", err.Error())
|
|
} else {
|
|
fmt.Printf("FATAL ERROR: %v", err.Error())
|
|
}
|
|
}
|
|
}
|