From f42be9d0d526fc6c0404c745a0375ffb5b19a888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20K=C3=B6ritz?= Date: Thu, 29 Aug 2019 09:55:04 +0200 Subject: [PATCH] Refactored, moved util to subfolder --- Camera.go | 43 ++++++----- RTPRelay.go | 2 +- ipcamera/ipcamera.go | 174 +++++++++++++++++++++++++++++++++++++++++++ protocol.go | 2 +- 4 files changed, 197 insertions(+), 24 deletions(-) create mode 100644 ipcamera/ipcamera.go diff --git a/Camera.go b/Camera.go index a6c8127..7aafa7f 100644 --- a/Camera.go +++ b/Camera.go @@ -1,4 +1,4 @@ -package main +package libipcamera import ( "encoding/binary" @@ -83,35 +83,30 @@ func (c *Camera) Connect() { c.HandleFirst(0x0112, aliveRequestHandler) go c.handleConnection() - - c.Login() } // Login will try to login to the camera control service func (c *Camera) Login() error { - loginAccept := make(chan bool, 1) + loginAccept := make(chan bool, 0) + c.Handle(LOGIN_ACCEPT, func(c *Camera, m *Message) (bool, error) { _, err := loginResultHandler(c, m) if err != nil { - return false, err + return RemoveHandler, err } loginAccept <- true - return KeepHandler, nil + return RemoveHandler, nil }) //TODO: Handle login error messages c.SendPacket(CreateLoginPacket(c.username, c.password)) select { - case loginSuccess := <-loginAccept: - if loginSuccess { - return nil - } + case <-loginAccept: + return nil case <-time.After(5 * time.Second): return errors.New("Login request timed out") } - - return errors.New("Login failed") } // IsConnected returns true if the camera connection has not been disconnected @@ -124,10 +119,16 @@ func (c *Camera) handleConnection() { var payload []byte for { + if c.disconnect { + break + } + // Read the header from the wire err := binary.Read(c.connection, binary.BigEndian, &header) if err != nil { - log.Printf("ERROR Reading from Camera: %s\n", err) + if !c.disconnect { + log.Printf("ERROR Reading from Camera: %s\n", err) + } break } @@ -164,7 +165,7 @@ func (c *Camera) handleConnection() { remainingMessageHandlers := make([]MessageHandler, 0) for _, handler := range c.messageHandlers[header.MessageType] { remove, err := handler(c, message) - if !remove { + if remove == KeepHandler { remainingMessageHandlers = append(remainingMessageHandlers, handler) } @@ -175,12 +176,8 @@ func (c *Camera) handleConnection() { } // replace handlers with all but the one-shot handlers c.messageHandlers[header.MessageType] = remainingMessageHandlers - - if c.disconnect { - break - } } - c.Log("Disconnecting") + c.Log("Disconnected") c.connected = false } @@ -210,7 +207,11 @@ func (c *Camera) addHandler(messageType uint32, handleFunc MessageHandler, prepe // Log will write to stdout if this camera has been set to be verbose func (c *Camera) Log(format string, data ...interface{}) { if c.verbose { - log.Printf(format+"\n", data) + if data != nil { + log.Printf(format+"\n", data) + } else { + log.Printf(format + "\n") + } } } @@ -396,10 +397,8 @@ func (c *Camera) SetVerbose(verbose bool) { } func aliveRequestHandler(camera *Camera, message *Message) (bool, error) { - camera.Log("Received Alive Request") responseHeader := CreateCommandHeader(0x0113) // Alive Response response := CreatePacket(responseHeader, []byte{}) - camera.Log("Sending Alive Response") return KeepHandler, camera.SendPacket(response) } diff --git a/RTPRelay.go b/RTPRelay.go index de42b2f..b376e8a 100644 --- a/RTPRelay.go +++ b/RTPRelay.go @@ -1,4 +1,4 @@ -package main +package libipcamera import ( "bytes" diff --git a/ipcamera/ipcamera.go b/ipcamera/ipcamera.go new file mode 100644 index 0000000..9ea7f16 --- /dev/null +++ b/ipcamera/ipcamera.go @@ -0,0 +1,174 @@ +package main + +import ( + "bufio" + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "log" + "net" + "net/http" + "os" + "path/filepath" + + libipcamera "github.com/jonas-koeritz/libipcamera" + "github.com/spf13/cobra" +) + +func main() { + var username string + var password string + var port int16 + var verbose bool + + var camera *libipcamera.Camera + + 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) { + defer camera.Disconnect() + relay := libipcamera.CreateRTPRelay(net.ParseIP("127.0.0.1"), 5220) + defer relay.Stop() + + camera.StartPreviewStream() + + bufio.NewReader(os.Stdin).ReadBytes('\n') + }, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + camera = libipcamera.CreateCamera(net.ParseIP(args[0]), int(port), username, password) + camera.SetVerbose(verbose) + camera.Connect() + camera.Login() + }, + PersistentPostRun: func(cmd *cobra.Command, args []string) { + camera.Disconnect() + }, + } + + 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") + + 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) { + files, err := camera.GetFileList() + if err != nil { + log.Printf("ERROR Receiving File List: %s\n", err) + return + } + + for _, file := range files { + fmt.Printf("%s\t%d\n", file.Path, file.Size) + } + }, + } + + 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.TakePicture() + }, + } + + 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.StartRecording() + }, + } + + 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.StopRecording() + }, + } + + 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) { + command, err := hex.DecodeString(args[0]) + if err != nil { + log.Printf("ERROR: %s\n", err) + return + } + + if len(command) >= 2 { + header := libipcamera.CreateCommandHeader(uint32(binary.BigEndian.Uint16(command[:2]))) + payload := command[2:] + packet := libipcamera.CreatePacket(header, payload) + log.Printf("Sending Command: %X\n", packet) + camera.SendPacket(packet) + } + + log.Printf("Waiting for Data, press ENTER to quit") + 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) { + files, err := camera.GetFileList() + if err != nil { + log.Printf("ERROR Receiving File List: %s\n", err) + return + } + + newestFile := files[len(files)-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/protocol.go b/protocol.go index 3953ff3..e95fe5a 100644 --- a/protocol.go +++ b/protocol.go @@ -1,4 +1,4 @@ -package main +package libipcamera import ( "bytes"