camera.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package lib
  2. import (
  3. "crypto/md5"
  4. "encoding/hex"
  5. "fmt"
  6. "io"
  7. "log"
  8. "net"
  9. "regexp"
  10. "strings"
  11. "time"
  12. )
  13. type Camera struct {
  14. host string
  15. port int
  16. alive time.Duration
  17. biosUri string
  18. fullUri string
  19. seq int
  20. sock net.Conn
  21. realm string
  22. nonce string
  23. auth string
  24. session string
  25. logger *log.Logger
  26. }
  27. func (cam *Camera) Init(host string, port int, alive int, logger *log.Logger) {
  28. cam.logger = logger
  29. cam.host, cam.port, cam.alive = host, port, time.Duration(alive)
  30. cam.biosUri = fmt.Sprintf("rtsp://%s:%d", host, port)
  31. cam.fullUri = fmt.Sprintf("rtsp://%s:%d/cam/realmonitor?channel=1&subtype=0", host, port)
  32. }
  33. func (cam *Camera) Run(stream chan []byte) {
  34. sock, err := net.Dial("tcp", fmt.Sprintf("%s:%d", cam.host, cam.port))
  35. if err != nil {
  36. cam.logger.Fatal("camera connect failed")
  37. }
  38. cam.seq, cam.sock = 1, sock
  39. cam.prepare(false)
  40. go cam.keepAliveThread()
  41. go cam.transportThread(stream)
  42. }
  43. func (cam *Camera) Stop() {
  44. if cam.sock == nil {
  45. return
  46. }
  47. // TEARDOWN
  48. request := fmt.Sprintf(
  49. "TEARDOWN %s RTSP/1.0\r\n"+
  50. "CSeq: %d\r\n"+
  51. `Authorization: Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s"\r\n`+
  52. "Session: %s\r\n"+
  53. "User-Agent: Tinger.HM-HZ\r\n\r\n",
  54. cam.fullUri, cam.seq, User, cam.realm,
  55. cam.nonce, cam.fullUri, cam.auth, cam.session)
  56. cam.send(request)
  57. // response := cam.receive() // no receive, as dirty data
  58. // close connection
  59. _ = cam.sock.Close()
  60. cam.seq, cam.sock = 1, nil
  61. }
  62. func (cam *Camera) show(title string, body string) {
  63. fmt.Printf("==============< %s> START ==============\n", title)
  64. fmt.Println(body)
  65. fmt.Printf("===============< %s> END ===============\n", title)
  66. }
  67. func (cam *Camera) hash(raw string) string {
  68. hash := md5.New()
  69. hash.Write([]byte(raw))
  70. hashBytes := hash.Sum(nil)
  71. return hex.EncodeToString(hashBytes)
  72. }
  73. func (cam *Camera) calcAuth(realm string, nonce string, method string) string {
  74. h1 := cam.hash(fmt.Sprintf("%s:%s:%s", User, realm, Pass))
  75. h2 := cam.hash(fmt.Sprintf("%s:%s", method, cam.biosUri))
  76. return cam.hash(fmt.Sprintf("%s:%s:%s", h1, nonce, h2))
  77. }
  78. func (cam *Camera) send(msg string) {
  79. _, err := cam.sock.Write([]byte(msg))
  80. if err != nil {
  81. // cam.logger.Print("message send failed")
  82. return
  83. }
  84. cam.seq++
  85. }
  86. func (cam *Camera) receive() string {
  87. buff := make([]byte, BlockSize)
  88. n, err := cam.sock.Read(buff)
  89. if err != nil {
  90. cam.logger.Fatal("camera message receive failed")
  91. }
  92. return string(buff[:n])
  93. }
  94. func (cam *Camera) prepare(verbose bool) {
  95. // OPTIONS 1: get realm, nonce
  96. request := fmt.Sprintf(
  97. "OPTIONS %s RTSP/1.0\r\n"+
  98. "CSeq: %d\r\n"+
  99. "User-Agent: Tinger.HM-HZ\r\n\r\n",
  100. cam.biosUri, cam.seq)
  101. cam.send(request)
  102. response := cam.receive()
  103. ptn := regexp.MustCompile(`Digest realm="(.+?)",nonce="(.+?)"\r\n`)
  104. search := ptn.FindStringSubmatch(response)
  105. if len(search) == 0 {
  106. cam.logger.Fatal("realm and nonce not matched")
  107. }
  108. cam.realm, cam.nonce = search[1], search[2]
  109. if verbose {
  110. cam.show("OPTIONS 1", request)
  111. cam.show("OPTIONS 1 RES", response)
  112. }
  113. // OPTIONS 2: auth
  114. cam.auth = cam.calcAuth(cam.realm, cam.nonce, "OPTIONS")
  115. request = fmt.Sprintf(
  116. "OPTIONS %s RTSP/1.0\r\n"+
  117. "Accept: application/sdp\r\n"+
  118. "CSeq: %d\r\n"+
  119. `Authorization: Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s"\r\n`+
  120. "User-Agent: Tinger.HM-HZ\r\n\r\n",
  121. cam.biosUri, cam.seq, User, cam.realm, cam.nonce, cam.biosUri, cam.auth)
  122. cam.send(request)
  123. response = cam.receive()
  124. if verbose {
  125. cam.show("OPTIONS 2", request)
  126. cam.show("OPTIONS 2 RES", response)
  127. }
  128. // DESCRIBE
  129. request = fmt.Sprintf(
  130. "DESCRIBE %s RTSP/1.0\r\n"+
  131. "CSeq: %d\r\n"+
  132. "User-Agent: Tinger.HM-HZ\r\n\r\n",
  133. cam.fullUri, cam.seq)
  134. cam.send(request)
  135. response = cam.receive()
  136. if verbose {
  137. cam.show("DESCRIBE", request)
  138. cam.show("DESCRIBE RES", response)
  139. }
  140. // SETUP: get session id
  141. request = fmt.Sprintf(
  142. "SETUP %s/trackID=0 RTSP/1.0\r\n"+
  143. "CSeq: %d\r\n"+
  144. "Transport: RTP/AVP/TCP;unicast;interleaved=0-1\r\n"+
  145. "User-Agent: Tinger.HM-HZ\r\n\r\n",
  146. cam.fullUri, cam.seq)
  147. cam.send(request)
  148. response = cam.receive()
  149. ptn = regexp.MustCompile(`Session: (\d+)`)
  150. search = ptn.FindStringSubmatch(response)
  151. if len(search) == 0 {
  152. cam.logger.Fatal("session not matched")
  153. }
  154. cam.session = search[1]
  155. if verbose {
  156. cam.show("SETUP", request)
  157. cam.show("SETUP RES", response)
  158. }
  159. // PLAY
  160. request = fmt.Sprintf(
  161. "PLAY %s RTSP/1.0\r\n"+
  162. "CSeq: %d\r\n"+
  163. "Session: %s\r\n"+
  164. "Range: npt=0.000-\r\n"+
  165. "User-Agent: Tigner,HN-HZ\r\n\r\n",
  166. cam.fullUri, cam.seq, cam.session)
  167. cam.send(request)
  168. response = cam.receive()
  169. if !strings.HasPrefix(response, "RTSP/1.0 200 OK") {
  170. cam.logger.Fatal("PLAY failed")
  171. }
  172. if verbose {
  173. cam.show("PLAY", request)
  174. cam.show("PLAY RES", response)
  175. }
  176. }
  177. func (cam *Camera) transportThread(stream chan []byte) {
  178. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  179. * a complicated situation: *
  180. * The actual reason is that every communication message are put in the same socket, *
  181. * which leads to <image stream data> and <other message>(mostly keep alive response message) *
  182. * will be mixed in the only socket message pool. *
  183. * So header[0] maybe not b"$"(ASCII: 36), in this case, it means that there are another *
  184. * response message which is not image data, what should we do is just find one by one *
  185. * until b"$" appears, the bytes data which between the wrong header[0] and b"$" are recognized *
  186. * as dirty data(are dropped in current process) *
  187. * *
  188. * resolution in future: *
  189. * store all received data in a bytes buffer *
  190. * data start with b"$" is image steam data *
  191. * it is easy to read every block of this type of data *
  192. * data start with b"RTSP 200 OK" is response data *
  193. * problem: how to locate the end(\r\n\r\n not always the real end of a message) *
  194. * you can try it, believe yourself! another words: you can you up. *
  195. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  196. defer cam.Stop()
  197. header := make([]byte, 4)
  198. buffer := make([]byte, BlockSize*2)
  199. syncer := make([]byte, 1)
  200. for {
  201. if count, err := io.ReadFull(cam.sock, header); err != nil || count != 4 {
  202. // cam.logger.Printf(fmt.Sprintf("header read error:\n%s\n", err))
  203. return
  204. }
  205. if header[0] != 36 {
  206. rid := 0
  207. for {
  208. if count, err := io.ReadFull(cam.sock, syncer); err != nil && count != 1 {
  209. // cam.logger.Printf("syncer read error:\n%s\n", err)
  210. return
  211. } else if syncer[0] == 36 {
  212. header[0] = 36
  213. if count, err = io.ReadFull(cam.sock, header[1:]); err != nil && count == 3 {
  214. // cam.logger.Printf("header remain read error:\n%s\n", err)
  215. return
  216. }
  217. break
  218. } else {
  219. rid++
  220. }
  221. }
  222. cam.logger.Printf("get rid of: %d\n", rid)
  223. }
  224. payloadLen := int(header[2])<<8 + int(header[3])
  225. fullLength := payloadLen + 4
  226. if fullLength > len(buffer) {
  227. buffer = make([]byte, fullLength)
  228. }
  229. copy(buffer, header)
  230. if count, err := io.ReadFull(cam.sock, buffer[4:fullLength]); err != nil || count != payloadLen {
  231. // cam.logger.Printf("payload read error:\n%s\n", err)
  232. return
  233. } else {
  234. stream <- buffer[:fullLength]
  235. }
  236. }
  237. }
  238. func (cam *Camera) keepAliveThread() {
  239. defer cam.Stop()
  240. for {
  241. time.Sleep(time.Second * cam.alive)
  242. if cam.sock == nil {
  243. continue
  244. }
  245. request := fmt.Sprintf(
  246. "OPTIONS %s RTSP/1.0\r\n"+
  247. "Accept: application/sdp\r\n"+
  248. "CSeq: %d\r\n"+
  249. `Authorization: Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s"\r\n`+
  250. "User-Agent: Tinger.HM-HZ\r\n\r\n",
  251. cam.biosUri, cam.seq, User, cam.realm, cam.nonce, cam.biosUri, cam.auth)
  252. cam.send(request)
  253. // response := cam.receive() // no receive, as dirty data
  254. }
  255. }