/* NAME RevidCLI.go DESCRIPTION See Readme.md AUTHORS Saxon A. Nelson-Milton Jack Richardson 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 ( "flag" "strconv" "revid" "bitbucket.org/ausocean/IoT/pi/netsender" ) // Indexes for flags const ( inputPtr = 0 inputCodecPtr = 1 outputPtr = 2 rtmpMethodPtr = 3 packetisationPtr = 4 quantizationModePtr = 5 verbosityPtr = 6 framesPerClipPrt = 7 rtmpUrlPtr = 8 bitratePtr = 9 outputFileNamePtr = 10 inputFileNamePtr = 11 heightPtr = 12 widthPtr = 13 frameRatePtr = 14 httpAddressPtr = 15 quantizationPtr = 18 timeoutPtr = 19 intraRefreshPeriodPtr = 20 ) // Other misc consts const ( sleepTime = 2 * 43200 defaultRunDuration = 2 * 43200 noOfConfigFlags = 20 revidStopTime = 5 ) // Globals var ( configReceived bool = true varSum int = 0 vars = make(map[string]string) revid revid.Revid config revid.Config ) func main() { flagNames := [19][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"}, } // Create the flags based on the flagNames array var configFlags [noOfConfigFlags](*string) for i := 0; i < 20; i++ { flags[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 flags[inputPtr] { case "Raspivid": config.Input = Raspivid case "Rtp": config.Input = Rtp case "File": config.Input = File } switch flags[inputCodecPtr] { case "H264Codec": config.InputCodec = H264Codec } switch flags[outputPtr] { case "File": config.Output = File case "Http": config.Output = Http case "Rtmp": config.Output = Rtmp } switch flags[rtmpMethodPtr] { case "Ffmpeg": config.RtmpMethod = Ffmpeg case "LibRtmp": config.RtmpMethod = LibRtmp } switch flags[packetisationPtr] { case "None": config.Packetisation = None case "Rtp": config.Packetisation = Rtp case "Flv": config.Packetisation = Flv } switch flags[quantisationModePtr] { case "QuantizationOn": config.Quantization = QuantizationOn case "QuantizationOff": config.Quantization = QuantizationOff } switch flags[verbosityPtr] { case "No": config.Verbosity = No case "Yes": config.Verbosity = Yes } config.FramesPerClip = flags[framesPerClipPtr] config.RtmpUrl = flags[rtmpUrlPtr] config.Bitrate = flags[bitratePtr] config.OutputFileName = flags[outputFileNamePtr] config.InputFileName = flags[inputFileNamePtr] config.Height = flags[heightPtr] config.Width = flags[widthPtr] config.FrameRate = flags[frameRatePtr] config.HttpAddress = flags[httpAddressPtr] config.Quantization = flags[quantisationPtr] config.Timeout = flags[timeoutPtr] config.IntraRefreshPeriod = flags[intraRefreshPeriodPtr] createRevidInstance() revid.Start() // Is the netsender flag been used ? if so run this loop for *netSenderFlagPtr { periodicNetsenderReport() sleepTime, err := 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 ) revid.Stop() } func netsenderInit() { netsender.InitRevidNetsender() netsender.ExternalReader = revidReportActions if err := netsender.GetConfig(); err != nil { //default to a config????? processManager.Logger.Log("Error", err.Error()) } else { configReceived = true; } } func periodicNetsenderReport(){ if !configReceived { if err := netsender.GetConfig(); err != nil { processManager.Logger.Log("Error", err.Error()) } else { configReceived = true; } } var reply string if reply, reconfig, err = netsender.Send(netsender.RequestPoll, criticalInputs); err != nil { processManager.Logger.Log("Error", err.Error()) } if cloudVarSum := netsender.GetVarSum(); cloudVarSum != varSum { if newVars, err := netsender.GetVars(); err != nil { processManager.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() { revid.Stop() time.Sleep( time.Duration(revidStopTime) * time.Second ) 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 { confog.IntraRefreshPeriod = value } } } createRevidInstance() revid.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 // } 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 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 * 100) stat, err = linuxproc.ReadStat("/proc/stat") if err != nil { return -1, errors.New("CPU Uage 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 not defined") } } func createRevidInstance(){ // Try to create the revid instance with the given config for revid, 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) } else { fmt.Printf("FATAL ERROR: %v", err) } } }