From a33f4d2b6d3055ab7fe10c259be2af94cf7bb180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20K=C3=B6ritz?= Date: Wed, 28 Aug 2019 15:44:07 +0200 Subject: [PATCH] Refactoring --- ipcamera/camera.go => camera.go | 2 +- ipcamera.go | 170 ++++++++++++++++++++++++++++ ipcamera/listener.go => listener.go | 38 ++++--- ipcamera/protocol.go => protocol.go | 2 +- 4 files changed, 194 insertions(+), 18 deletions(-) rename ipcamera/camera.go => camera.go (99%) create mode 100644 ipcamera.go rename ipcamera/listener.go => listener.go (72%) rename ipcamera/protocol.go => protocol.go (98%) diff --git a/ipcamera/camera.go b/camera.go similarity index 99% rename from ipcamera/camera.go rename to camera.go index 46d3f0c..582bdcb 100644 --- a/ipcamera/camera.go +++ b/camera.go @@ -1,4 +1,4 @@ -package ipcamera +package main import ( "encoding/binary" diff --git a/ipcamera.go b/ipcamera.go new file mode 100644 index 0000000..fc8ec8b --- /dev/null +++ b/ipcamera.go @@ -0,0 +1,170 @@ +package main + +import ( + "bufio" + "encoding/binary" + "encoding/hex" + "io" + "log" + "net" + "net/http" + "os" + "path/filepath" + + "github.com/spf13/cobra" +) + +func main() { + var username string + var password string + var port int16 + + var rootCmd = &cobra.Command{ + Use: "ipcamera [Cameras IP Address]", + Short: "ipcamera is a tool to stream the video preview of cheap action cameras without the mobile application", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + relay := CreateRTPRelay(net.ParseIP("127.0.0.1"), 5220) + defer relay.Stop() + + camera := Camera{IPAddress: args[0], Port: (int)(port), Verbose: true} + + log.Printf("Using Camera: %+v\n", camera) + + camera.Connect(username, password) + bufio.NewReader(os.Stdin).ReadBytes('\n') + }, + } + + 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") + + var ls = &cobra.Command{ + Use: "ls [Cameras IP Address]", + Short: "List files stored on the cameras SD-Card", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + camera := Camera{IPAddress: args[0], Port: (int)(port), Verbose: false} + + camera.Connect(username, password) + camera.RequestFileList() + camera.WaitForMessage(0xA027) + //camera.StoredFiles + }, + } + + var still = &cobra.Command{ + Use: "still [Cameras IP Address]", + Short: "Take a still image and save to SD-Card", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + camera := Camera{IPAddress: args[0], Port: (int)(port), Verbose: true} + camera.Connect(username, password) + camera.TakePicture() + camera.WaitForMessage(0xA039) + }, + } + + var record = &cobra.Command{ + Use: "record [Cameras IP Address]", + Short: "Start recording video to SD-Card", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + camera := Camera{IPAddress: args[0], Port: (int)(port), Verbose: true} + camera.Connect(username, password) + camera.StartRecording() + camera.WaitForMessage(0xA03B) + }, + } + + var stop = &cobra.Command{ + Use: "stop [Cameras IP Address]", + Short: "Stop recording video to SD-Card", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + camera := Camera{IPAddress: args[0], Port: (int)(port), Verbose: true} + camera.Connect(username, password) + camera.StopRecording() + camera.WaitForMessage(0xA03B) + }, + } + + var cmd = &cobra.Command{ + Use: "cmd [RAW Command] [Cameras IP Address]", + Short: "Send a raw command to the camera", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + camera := Camera{IPAddress: args[1], Port: (int)(port), Verbose: true} + + camera.Connect(username, password) + command, err := hex.DecodeString(args[0]) + if err != nil { + log.Printf("ERROR: %s\n", err) + return + } + + if len(command) >= 2 { + header := CreateCommandHeader(uint32(binary.BigEndian.Uint16(command[:2]))) + payload := command[2:] + log.Printf("Waiting for Login to finish") + camera.WaitForMessage(0x0111) + packet := CreatePacket(header, payload) + log.Printf("Sending Command: %X\n", packet) + camera.SendPacket(packet) + } + + bufio.NewReader(os.Stdin).ReadBytes('\n') + }, + } + + var fetch = &cobra.Command{ + Use: "fetch [Cameras IP Address]", + Short: "List files stored on the cameras SD-Card", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + camera := Camera{IPAddress: args[0], Port: (int)(port), Verbose: false} + + camera.Connect(username, password) + camera.RequestFileList() + camera.WaitForMessage(0xA027) + newestFile := camera.StoredFiles[len(camera.StoredFiles)-1].Path + url := "http://" + args[0] + newestFile + log.Printf("Downloading latest File: %s\n", url) + downloadFile(filepath.Base(newestFile), url) + }, + } + + rootCmd.AddCommand(ls) + rootCmd.AddCommand(cmd) + rootCmd.AddCommand(still) + rootCmd.AddCommand(stop) + rootCmd.AddCommand(fetch) + rootCmd.AddCommand(record) + + if err := rootCmd.Execute(); err != nil { + log.Println(err) + os.Exit(1) + } +} + +func downloadFile(filepath string, url string) error { + + // Get the data + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + // Create the file + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + // Write the body to file + _, err = io.Copy(out, resp.Body) + return err +} diff --git a/ipcamera/listener.go b/listener.go similarity index 72% rename from ipcamera/listener.go rename to listener.go index 9ebe82a..de42b2f 100644 --- a/ipcamera/listener.go +++ b/listener.go @@ -1,4 +1,4 @@ -package ipcamera +package main import ( "bytes" @@ -9,37 +9,43 @@ import ( "net" ) -// StreamListener holds information on the receiving stream listener -type StreamListener struct { - close bool +// RTPRelay holds information on the relaying stream listener +type RTPRelay struct { + close bool + targetIP net.IP + targetPort int } -// CreateStreamListener creates a UDP listener that handles live data from the camera -func CreateStreamListener() StreamListener { +// CreateRTPRelay creates a UDP listener that handles live data +// from the camera and forwards it as an RTP stream +func CreateRTPRelay(targetAddress net.IP, targetPort int) RTPRelay { conn, err := net.ListenPacket("udp", ":6669") if err != nil { log.Printf("ERROR: %s\n", err) } - streamListener := StreamListener{} + relay := RTPRelay{ + targetIP: targetAddress, + targetPort: targetPort, + } if err != nil { log.Printf("ERROR: %s\n", err) } - go handleCameraStream(streamListener, conn) + go handleCameraStream(relay, conn) - return streamListener + return relay } -func handleCameraStream(listener StreamListener, conn net.PacketConn) { +func handleCameraStream(relay RTPRelay, conn net.PacketConn) { buffer := make([]byte, 2048) header := StreamHeader{} var payload []byte rtpTarget := net.UDPAddr{ - IP: net.ParseIP("127.0.0.1"), - Port: 5220, + IP: relay.targetIP, + Port: relay.targetPort, } rtpSource, _ := net.ResolveUDPAddr("udp", "127.0.0.1:5000") rtpConn, _ := net.DialUDP("udp", rtpSource, &rtpTarget) @@ -88,13 +94,13 @@ func handleCameraStream(listener StreamListener, conn net.PacketConn) { log.Printf("Received Unknown Message: %+v\n", header) log.Printf("Payload:\n%s\n", hex.Dump(payload)) } - if listener.close { + if relay.close { break } } } -// Close stops listening for packets -func (l *StreamListener) Close() { - l.close = true +// Stop stops listening for packets +func (r *RTPRelay) Stop() { + r.close = true } diff --git a/ipcamera/protocol.go b/protocol.go similarity index 98% rename from ipcamera/protocol.go rename to protocol.go index 972a646..213ce89 100644 --- a/ipcamera/protocol.go +++ b/protocol.go @@ -1,4 +1,4 @@ -package ipcamera +package main import ( "bytes"