av/RevidCLI.go

469 lines
13 KiB
Go
Raw Normal View History

2018-04-19 10:03:12 +03:00
/*
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 (
2018-04-19 12:13:24 +03:00
"fmt"
2018-04-19 12:04:08 +03:00
"errors"
"os/exec"
"flag"
"strconv"
2018-04-19 11:19:36 +03:00
"time"
2018-04-19 08:58:16 +03:00
2018-04-19 11:19:36 +03:00
"bitbucket.org/ausocean/av/revid"
2018-04-26 09:26:05 +03:00
"bitbucket.org/ausocean/utils/smartLogger"
2018-04-19 08:58:16 +03:00
"bitbucket.org/ausocean/IoT/pi/netsender"
2018-04-19 12:04:08 +03:00
linuxproc "github.com/c9s/goprocinfo/linux"
)
2018-04-19 11:11:18 +03:00
// Indexes for configFlags
const (
inputPtr = 0
inputCodecPtr = 1
outputPtr = 2
rtmpMethodPtr = 3
2018-04-19 11:19:36 +03:00
packetizationPtr = 4
quantizationModePtr = 5
verbosityPtr = 6
2018-04-19 11:24:44 +03:00
framesPerClipPtr = 7
rtmpUrlPtr = 8
bitratePtr = 9
outputFileNamePtr = 10
inputFileNamePtr = 11
heightPtr = 12
widthPtr = 13
frameRatePtr = 14
httpAddressPtr = 15
2018-04-19 12:31:53 +03:00
quantizationPtr = 16
timeoutPtr = 17
intraRefreshPeriodPtr = 18
2018-04-30 23:37:37 +03:00
rotatePtr = 19
)
// Other misc consts
const (
sleepTime = 2 * 43200
defaultRunDuration = 2 * 43200
2018-04-30 23:37:37 +03:00
noOfConfigconfigFlags = 20
revidStopTime = 5
2018-04-25 10:25:42 +03:00
prepTime = 20
2018-04-26 10:10:19 +03:00
loggerVerbosity = 3
)
2018-04-19 12:04:08 +03:00
const (
lightRelayPin = 19
ds18b20Temp = 60
dht11Temp = 40
dht11Hum = 41
dht22Temp = 50
dht22Hum = 51
dhtPin = 22
cpuTemp = 21
cpuUsage = 20
priority = 25
)
// Globals
var (
2018-04-19 08:58:16 +03:00
configReceived bool = true
varSum int = 0
vars = make(map[string]string)
2018-04-19 11:11:18 +03:00
revidInst revid.Revid
2018-04-19 10:25:37 +03:00
config revid.Config
2018-04-19 08:58:16 +03:00
)
func main() {
2018-04-19 12:31:53 +03:00
flagNames := [noOfConfigconfigFlags][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"},
2018-04-30 23:37:37 +03:00
{"Rotate", "Rotate video the specified number of degrees"},
}
2018-04-19 11:11:18 +03:00
// Create the configFlags based on the flagNames array
2018-04-19 12:28:59 +03:00
configFlags := make([](*string),noOfConfigconfigFlags)
2018-04-19 12:31:53 +03:00
for i := 0; i < noOfConfigconfigFlags; i++ {
2018-04-19 11:11:18 +03:00
configFlags[i] = flag.String(flagNames[i][0], "", flagNames[i][1])
}
2018-04-19 10:03:12 +03:00
// Do we want a netsender session
netSenderFlagPtr := flag.Bool("netSender", false, "Are we checking vars through netsender?")
2018-04-19 10:03:12 +03:00
// 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":
2018-04-19 11:16:26 +03:00
config.Input = revid.Raspivid
case "Rtp":
2018-04-19 11:16:26 +03:00
config.Input = revid.Rtp
case "File":
2018-04-19 11:16:26 +03:00
config.Input = revid.File
2018-04-19 12:37:25 +03:00
case "":
default:
fmt.Println("Bad input argument!")
}
switch *configFlags[inputCodecPtr] {
case "H264Codec":
2018-04-19 11:16:26 +03:00
config.InputCodec = revid.H264Codec
2018-04-19 12:37:25 +03:00
case "":
default:
fmt.Println("Bad input codec argument!")
}
switch *configFlags[outputPtr] {
case "File":
2018-04-19 11:16:26 +03:00
config.Output = revid.File
case "Http":
2018-04-19 11:16:26 +03:00
config.Output = revid.Http
2018-04-19 12:39:48 +03:00
case "NativeRtmp":
config.Output = revid.NativeRtmp
case "FfmpegRtmp":
config.Output = revid.FfmpegRtmp
2018-04-19 12:37:25 +03:00
case "":
default:
fmt.Println("Bad output argument!")
}
switch *configFlags[rtmpMethodPtr] {
case "Ffmpeg":
2018-04-19 11:16:26 +03:00
config.RtmpMethod = revid.Ffmpeg
case "LibRtmp":
2018-04-19 11:16:26 +03:00
config.RtmpMethod = revid.LibRtmp
2018-04-19 12:37:25 +03:00
case "":
default:
fmt.Println("Bad rtmp method argument!")
}
2018-04-19 11:24:44 +03:00
switch *configFlags[packetizationPtr] {
case "None":
2018-04-19 11:19:36 +03:00
config.Packetization = revid.None
case "Rtp":
2018-04-19 11:19:36 +03:00
config.Packetization = revid.Rtp
case "Flv":
2018-04-19 11:19:36 +03:00
config.Packetization = revid.Flv
2018-04-19 12:37:25 +03:00
case "":
default:
fmt.Println("Bad packetization argument!")
}
2018-04-19 11:19:36 +03:00
switch *configFlags[quantizationModePtr] {
case "QuantizationOn":
2018-04-19 11:24:44 +03:00
config.QuantizationMode = revid.QuantizationOn
case "QuantizationOff":
2018-04-19 11:24:44 +03:00
config.QuantizationMode = revid.QuantizationOff
2018-04-19 12:37:25 +03:00
case "":
default:
fmt.Println("Bad quantization mode argument!")
}
switch *configFlags[verbosityPtr] {
case "No":
2018-04-19 11:16:26 +03:00
config.Verbosity = revid.No
case "Yes":
2018-04-19 11:16:26 +03:00
config.Verbosity = revid.Yes
2018-04-19 12:37:25 +03:00
case "":
default:
fmt.Println("Bad verbosity argument!")
}
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]
2018-04-19 11:37:10 +03:00
config.Quantization = *configFlags[quantizationPtr]
config.Timeout = *configFlags[timeoutPtr]
config.IntraRefreshPeriod = *configFlags[intraRefreshPeriodPtr]
2018-04-30 23:37:37 +03:00
config.Rotate = *configFlags[rotatePtr]
2018-04-25 05:58:30 +03:00
// Also give the config a logger object
2018-04-26 09:24:54 +03:00
config.Logger = smartLogger.New(loggerVerbosity, smartLogger.White)
2018-04-25 05:58:30 +03:00
time.Sleep(time.Duration(prepTime) * time.Second)
2018-04-19 10:03:12 +03:00
createRevidInstance()
2018-04-19 11:11:18 +03:00
revidInst.Start()
2018-04-20 09:53:51 +03:00
2018-04-20 05:20:33 +03:00
//init netsender
if *netSenderFlagPtr{
netsenderInit()
}
2018-04-20 09:53:51 +03:00
2018-04-19 08:58:16 +03:00
2018-04-19 10:03:12 +03:00
// Is the netsender flag been used ? if so run this loop
for *netSenderFlagPtr {
2018-04-19 08:58:16 +03:00
periodicNetsenderReport()
2018-04-19 12:14:56 +03:00
sleepTime, _ := strconv.Atoi(netsender.GetConfigParam("monPeriod"))
2018-04-19 08:58:16 +03:00
time.Sleep(time.Duration(sleepTime) * time.Second)
}
2018-04-19 10:03:12 +03:00
// 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 )
2018-04-19 11:24:44 +03:00
revidInst.Stop()
2018-04-19 08:58:16 +03:00
}
func netsenderInit() {
2018-04-20 05:13:29 +03:00
//initialize netsender and assign function to check X pins
2018-04-30 05:38:00 +03:00
config.Logger = netsender.GetLogger()
2018-04-19 08:58:16 +03:00
netsender.InitRevidNetsender()
netsender.ExternalReader = revidReportActions
2018-04-20 05:13:29 +03:00
//try get initial config
2018-04-19 08:58:16 +03:00
if err := netsender.GetConfig(); err != nil {
//default to a config?????
2018-04-19 11:59:51 +03:00
config.Logger.Log("Error", err.Error())
2018-04-19 08:58:16 +03:00
} else {
configReceived = true;
}
}
2018-04-20 09:53:51 +03:00
//periodicNetsenderReport is called by the main function every monPeriod seconds. It makes sure a config has been recieved, and then
2018-04-20 05:13:29 +03:00
//reports back CPU stats and updates vars
2018-04-19 08:58:16 +03:00
func periodicNetsenderReport(){
if !configReceived {
if err := netsender.GetConfig(); err != nil {
2018-04-19 11:59:51 +03:00
config.Logger.Log("Error", err.Error())
2018-04-19 08:58:16 +03:00
} else {
configReceived = true;
}
}
2018-04-30 06:14:21 +03:00
//inputs := []string{"X20", "X21"}
inputs := netsender.GetConfigParam("inputs")
2018-04-19 12:14:56 +03:00
if _, _, err := netsender.Send(netsender.RequestPoll, inputs); err != nil {
2018-04-19 11:59:51 +03:00
config.Logger.Log("Error", err.Error())
2018-04-19 08:58:16 +03:00
}
if cloudVarSum := netsender.GetVarSum(); cloudVarSum != varSum {
if newVars, err := netsender.GetVars(); err != nil {
2018-04-19 11:59:51 +03:00
config.Logger.Log("Error", err.Error())
2018-04-19 08:58:16 +03:00
} else {
varSum = cloudVarSum
for newVar, value := range newVars {
if currentValue, varExists := vars[newVar]; !varExists || currentValue != value {
vars[newVar] = value
}
}
}
updateRevid()
}
2018-04-19 08:58:16 +03:00
}
2018-04-20 05:13:29 +03:00
2018-04-19 08:58:16 +03:00
func updateRevid() {
2018-04-19 11:11:18 +03:00
revidInst.Stop()
time.Sleep( time.Duration(revidStopTime) * time.Second )
2018-04-20 05:13:29 +03:00
//look through var map and update revid where needed
2018-04-20 11:23:59 +03:00
fmt.Printf("These are the vars: %v\n", vars)
2018-04-19 08:58:16 +03:00
for key, value := range vars {
switch key {
case "FramesPerClip":
2018-04-19 10:03:12 +03:00
asInt,err := strconv.Atoi(value)
if asInt > 0 && err == nil {
config.FramesPerClip = value
}
2018-04-19 08:58:16 +03:00
case "RtmpUrl":
2018-04-20 09:53:51 +03:00
fmt.Println("@@@@@@ Updating the rtmp url!")
2018-04-19 08:58:16 +03:00
config.RtmpUrl = value
case "Bitrate":
2018-04-19 10:03:12 +03:00
asInt,err := strconv.Atoi(value)
if asInt > 0 && err == nil {
config.Bitrate = value
}
2018-04-19 08:58:16 +03:00
case "OutputFileName":
config.OutputFileName = value
case "InputFileName":
config.InputFileName = value
case "Height":
2018-04-19 10:03:12 +03:00
asInt,err := strconv.Atoi(value)
if asInt > 0 && err == nil {
config.Height = value
}
2018-04-19 08:58:16 +03:00
case "Width":
2018-04-19 10:03:12 +03:00
asInt,err := strconv.Atoi(value)
if asInt > 0 && err == nil {
config.Width = value
}
2018-04-19 08:58:16 +03:00
case "FrameRate":
2018-04-19 10:03:12 +03:00
asInt,err := strconv.Atoi(value)
if asInt > 0 && err == nil {
config.FrameRate = value
}
2018-04-19 08:58:16 +03:00
case "HttpAddress":
config.HttpAddress = value
case "Quantization":
2018-04-19 10:03:12 +03:00
asInt,err := strconv.Atoi(value)
if asInt > 0 && err == nil {
config.Quantization = value
}
2018-04-19 08:58:16 +03:00
case "Timeout":
2018-04-19 10:03:12 +03:00
asInt,err := strconv.Atoi(value)
if asInt > 0 && err == nil {
config.Timeout = value
}
2018-04-19 08:58:16 +03:00
case "IntraRefreshPeriod":
2018-04-19 10:03:12 +03:00
asInt,err := strconv.Atoi(value)
if asInt > 0 && err == nil {
2018-04-19 11:59:51 +03:00
config.IntraRefreshPeriod = value
2018-04-19 10:03:12 +03:00
}
2018-04-19 08:58:16 +03:00
}
}
2018-04-19 10:03:12 +03:00
createRevidInstance()
2018-04-19 11:11:18 +03:00
revidInst.Start()
2018-04-19 08:58:16 +03:00
}
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
// }
2018-04-20 05:13:29 +03:00
//function to measure temp of cpu
2018-04-19 08:58:16 +03:00
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
2018-04-20 05:13:29 +03:00
//function to measure usage of cpu
2018-04-19 08:58:16 +03:00
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
2018-04-19 08:58:16 +03:00
idle1 := stat.CPUStatAll.Idle
time.Sleep(time.Millisecond * 100)
stat, err = linuxproc.ReadStat("/proc/stat")
if err != nil {
2018-04-19 12:04:08 +03:00
return -1, errors.New("CPU Usage Read Err: " + err.Error())
2018-04-19 08:58:16 +03:00
}
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
2018-04-19 08:58:16 +03:00
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:
2018-04-20 05:17:19 +03:00
return -1, errors.New("External pin" + strconv.Itoa(pin) + " not defined")
2018-04-19 08:58:16 +03:00
}
}
2018-04-19 10:03:12 +03:00
func createRevidInstance(){
// Try to create the revid instance with the given config
2018-04-19 12:14:56 +03:00
var err error
for revidInst, err = revid.NewRevid(config); err != nil; {
2018-04-19 10:03:12 +03:00
// If the config does have a logger, use it to output error, otherwise
// just output to std output
if config.Logger != nil {
2018-04-19 12:13:24 +03:00
config.Logger.Log("FATAL ERROR", err.Error())
2018-04-19 10:03:12 +03:00
} else {
2018-04-19 12:13:24 +03:00
fmt.Printf("FATAL ERROR: %v", err.Error())
2018-04-19 10:03:12 +03:00
}
}
}