diff --git a/.gitignore b/.gitignore index 4e7458f..a1dd7fd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.jpg *.jpeg actioncam +*.prof diff --git a/actioncam.go b/actioncam.go index aaf2f6a..1012ae6 100644 --- a/actioncam.go +++ b/actioncam.go @@ -11,6 +11,8 @@ import ( "net/http" "os" "path/filepath" + "runtime" + "runtime/pprof" "github.com/jonas-koeritz/actioncam/libipcamera" "github.com/jonas-koeritz/actioncam/rtsp" @@ -30,6 +32,10 @@ func main() { var password string var port int16 var verbose bool + var cpuprofile string + var memoryprofile string + + var cpuprofileFile *os.File var camera *libipcamera.Camera @@ -46,18 +52,50 @@ func main() { bufio.NewReader(os.Stdin).ReadBytes('\n') }, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + if cpuprofile != "" { + cpuprofileFile, err := os.Create(cpuprofile) + if err != nil { + log.Printf("Could not create CPU profiling file: %s\n", err) + return + } + err = pprof.StartCPUProfile(cpuprofileFile) + if err != nil { + log.Printf("Could not start CPU profiling: %s\n", err) + } + } + }, PreRun: func(cmd *cobra.Command, args []string) { camera = connectAndLogin(net.ParseIP(args[0]), int(port), username, password, verbose) }, PostRun: func(cmd *cobra.Command, args []string) { camera.Disconnect() }, + PersistentPostRun: func(cmd *cobra.Command, args []string) { + pprof.StopCPUProfile() + cpuprofileFile.Close() + + runtime.GC() + if memoryprofile != "" { + f, err := os.Create(memoryprofile) + if err != nil { + log.Printf("Could not create Memory profiling file: %s\n", err) + return + } + err = pprof.WriteHeapProfile(f) + if err != nil { + log.Printf("Could not start Memory profiling: %s\n", err) + } + } + }, } rootCmd.PersistentFlags().Int16VarP(&port, "port", "P", 6666, "Specify an alternative camera port to connect to") rootCmd.PersistentFlags().StringVarP(&username, "username", "u", "admin", "Specify the camera username") rootCmd.PersistentFlags().StringVarP(&password, "password", "p", "12345", "Specify the camera password") rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Print verbose output") + rootCmd.PersistentFlags().StringVarP(&cpuprofile, "cpuprofile", "c", "", "Profile CPU usage") + rootCmd.PersistentFlags().StringVarP(&memoryprofile, "memoryprofile", "m", "", "Profile memory usage") var ls = &cobra.Command{ Use: "ls [Cameras IP Address]", diff --git a/libipcamera/RTPRelay.go b/libipcamera/RTPRelay.go index 6b73fa2..397ca95 100644 --- a/libipcamera/RTPRelay.go +++ b/libipcamera/RTPRelay.go @@ -42,6 +42,8 @@ func CreateRTPRelay(targetAddress net.IP, targetPort int) *RTPRelay { func handleCameraStream(relay RTPRelay, conn net.PacketConn) { buffer := make([]byte, 2048) + packetReader := bytes.NewReader(buffer) + header := StreamHeader{} var payload []byte @@ -58,7 +60,8 @@ func handleCameraStream(relay RTPRelay, conn net.PacketConn) { var sequenceNumber uint16 var elapsed uint32 - frameBuffer := []byte{} + frameBuffer := bytes.Buffer{} + packetBuffer := bytes.Buffer{} for { if relay.close { @@ -67,18 +70,20 @@ func handleCameraStream(relay RTPRelay, conn net.PacketConn) { } conn.ReadFrom(buffer) - packetReader := bytes.NewReader(buffer) + packetReader.Reset(buffer) + binary.Read(packetReader, binary.BigEndian, &header) if header.Magic != 0xBCDE { log.Printf("Received message with invalid magic (%x).", header.Magic) break } + if header.Length > 0 { payload = make([]byte, header.Length) - bytesRead, err := io.ReadFull(packetReader, payload) + _, err := io.ReadFull(packetReader, payload) if err != nil { - log.Printf("Read Error: %s, %d bytes\n", err, bytesRead) + log.Printf("Read Error: %s\n", err) break } } else { @@ -87,19 +92,26 @@ func handleCameraStream(relay RTPRelay, conn net.PacketConn) { switch header.MessageType { case 0x0001: // H.264 Data - frameBuffer = append(frameBuffer, payload...) + frameBuffer.Write(payload) case 0x0002: // Time - packet := bytes.Buffer{} - packet.Write([]byte{0x80, 0x63}) - binary.Write(&packet, binary.BigEndian, sequenceNumber) - binary.Write(&packet, binary.BigEndian, (uint32)(elapsed*90)) - binary.Write(&packet, binary.BigEndian, (uint64(0))) - packet.Write(frameBuffer) - rtpConn.Write(packet.Bytes()) - frameBuffer = []byte{} + // Append the Framebuffer + packetBuffer.Write(frameBuffer.Bytes()) + + // Send out the packet + rtpConn.Write(packetBuffer.Bytes()) + + // Prepare the next packet + packetBuffer.Reset() + packetBuffer.Write([]byte{0x80, 0x63}) + binary.Write(&packetBuffer, binary.BigEndian, sequenceNumber+1) + binary.Write(&packetBuffer, binary.BigEndian, (uint32)(elapsed*90)) + binary.Write(&packetBuffer, binary.BigEndian, (uint64(0))) + + // Reset the Framebuffer + frameBuffer.Reset() sequenceNumber++ + elapsed = binary.LittleEndian.Uint32(payload[12:]) - //log.Printf("Elapsed: %d (%x)\n", elapsed, payload[12:]) default: log.Printf("Received Unknown Message: %+v\n", header) log.Printf("Payload:\n%s\n", hex.Dump(payload))