252 lines
6.2 KiB
Go
252 lines
6.2 KiB
Go
package tutk
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"time"
|
|
)
|
|
|
|
func (c *Conn) WriteCh0(msg []byte) error {
|
|
binary.LittleEndian.PutUint16(msg[6:], c.seqSendCh0)
|
|
c.seqSendCh0++
|
|
return c.Write(msg)
|
|
}
|
|
|
|
func (c *Conn) WriteCh1(msg []byte) error {
|
|
binary.LittleEndian.PutUint16(msg[6:], c.seqSendCh1)
|
|
c.seqSendCh1++
|
|
msg[14] = 1 // channel
|
|
return c.Write(msg)
|
|
}
|
|
|
|
func (c *Conn) msgConnectByUID(uid []byte, i byte) []byte {
|
|
const size = 68 // or 52 or 68 or 88
|
|
b := make([]byte, size)
|
|
copy(b, "\x04\x02\x19\x02")
|
|
b[4] = size - 16
|
|
copy(b[8:], "\x01\x06\x21\x00")
|
|
copy(b[16:], uid)
|
|
copy(b[52:], "\x00\x03\x01\x02") // or 07000303 or 01010204
|
|
copy(b[56:], c.sid[8:])
|
|
b[64] = i // 1 or 2
|
|
return b
|
|
}
|
|
|
|
func (c *Conn) msgAvClientStart() []byte {
|
|
const size = 566 + 32
|
|
msg := c.msg(size)
|
|
|
|
cmd := msg[msgHhrSize:]
|
|
copy(cmd, "\x00\x00\x0b\x00")
|
|
binary.LittleEndian.PutUint16(cmd[16:], size-52)
|
|
//cmd[18] = 1 // ???
|
|
binary.LittleEndian.PutUint32(cmd[20:], uint32(time.Now().UnixMilli()))
|
|
|
|
// important values for some cameras (not for df3)
|
|
data := cmd[cmdHdrSize:]
|
|
copy(data, "Miss")
|
|
copy(data[257:], "client")
|
|
|
|
// 0100000004000000fb071f000000000000000000000003000000000001000000
|
|
cfg := msg[566:]
|
|
cfg[0] = 0 // 0 - simple proto, 1 - complex proto with "0Cxx" commands
|
|
cfg[4] = 4
|
|
copy(cfg[8:], "\xfb\x07\x1f\x00")
|
|
cfg[22] = 3
|
|
cfg[28] = 1
|
|
return msg
|
|
}
|
|
|
|
func (c *Conn) msg(size uint16) []byte {
|
|
b := make([]byte, size)
|
|
copy(b, "\x04\x02\x19\x0a")
|
|
binary.LittleEndian.PutUint16(b[4:], size-16)
|
|
copy(b[8:], "\x07\x04\x21\x00")
|
|
copy(b[12:], c.sid)
|
|
return b
|
|
}
|
|
|
|
const (
|
|
msgPing = iota + 1
|
|
msgClientStart00
|
|
msgClientStart20
|
|
msgCommand
|
|
msgCounters
|
|
msgMediaChunk
|
|
msgMediaFrame
|
|
msgMediaLost
|
|
msgCh5
|
|
msgUnknown0007
|
|
msgUnknown0008
|
|
msgUnknown0010
|
|
msgUnknown0013
|
|
msgUnknown0a08
|
|
msgDafang0012
|
|
msgDafang0071
|
|
)
|
|
|
|
// handleMsg will return parsed msg type or zero
|
|
func (c *Conn) handleMsg(msg []byte) int8 {
|
|
//log.Printf("<- %x", msg)
|
|
// off sample
|
|
// 0 0402 tutk magic
|
|
// 2 120a tutk version (120a, 190a...)
|
|
// 4 0800 msg size = len(b)-16
|
|
// 6 0000 channel seq
|
|
// 8 28041200 msg type
|
|
// 14 0100 channel (not all msg)
|
|
// 28 0700 msg data (not all msg)
|
|
switch msg[8] {
|
|
case 0x28:
|
|
_ = c.Write(msgAckPing(msg))
|
|
return msgPing
|
|
case 0x08:
|
|
switch ch := msg[14]; ch {
|
|
case 0:
|
|
return c.handleCh0(msg[28:])
|
|
case 1:
|
|
return c.handleCh1(msg[28:])
|
|
case 5:
|
|
return c.handleCh5(msg)
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (c *Conn) handleCh1(cmd []byte) int8 {
|
|
// Channel 1 used for two-way audio. It's important:
|
|
// - answer on 0000 command with exact config response (can't set simple proto)
|
|
// - send 0012 command at start
|
|
// - respond on every 0008 command for smooth playback
|
|
switch cid := string(cmd[:2]); cid {
|
|
case "\x00\x00": // client start
|
|
_ = c.WriteCh1(c.msgAck0000(cmd))
|
|
_ = c.WriteCh1(c.msg0012())
|
|
return msgClientStart00
|
|
case "\x00\x07": // time sync without data
|
|
_ = c.WriteCh1(c.msgAck0007(cmd))
|
|
return msgUnknown0007
|
|
case "\x00\x08": // time sync with data
|
|
_ = c.WriteCh1(c.msgAck0008(cmd))
|
|
return msgUnknown0008
|
|
case "\x00\x13": // ack for 0012
|
|
return msgUnknown0013
|
|
case "\x00\x20": // client start2
|
|
//_ = c.WriteCh1(c.msgAck0020(cmd))
|
|
return msgClientStart20
|
|
case "\x09\x00": // counters sync
|
|
return msgCounters
|
|
case "\x0a\x08": // unknown
|
|
_ = c.WriteCh1(c.msgAck0A08(cmd))
|
|
return msgUnknown0a08
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (c *Conn) handleCh5(msg []byte) int8 {
|
|
if len(msg) != 48 {
|
|
return 0
|
|
}
|
|
|
|
// <- [48] 0402190a 20000400 07042100 7ecc05000c0000007ecc93c456c2561f 5a97c2f101050000000000000000000000010000
|
|
// -> [48] 0402190a 20000400 08041200 7ecc05000c0000007ecc93c456c2561f 5a97c2f141050000000000000000000000010000
|
|
copy(msg[8:], "\x07\x04\x21\x00")
|
|
msg[32] = 0x41
|
|
_ = c.Write(msg)
|
|
return msgCh5
|
|
}
|
|
|
|
const msgHhrSize = 28
|
|
const cmdHdrSize = 24
|
|
|
|
func (c *Conn) msgAck0000(msg28 []byte) []byte {
|
|
// <- 000008000000000000000000000000001a0200004f47c714 ... 00000000000000000100000004000000fb071f00000000000000000000000300
|
|
// -> 00140b00000000000000000000000000200000004f47c714 00000000000000000100000004000000fb071f00000000000000000000000300
|
|
const cmdDataSize = 32
|
|
msg := c.msg(msgHhrSize + cmdHdrSize + cmdDataSize)
|
|
|
|
cmd := msg[msgHhrSize:]
|
|
copy(cmd, "\x00\x14\x0b\x00")
|
|
cmd[16] = cmdDataSize
|
|
copy(cmd[20:], msg28[20:24]) // request id (random)
|
|
|
|
// Important to answer with same data.
|
|
data := cmd[cmdHdrSize:]
|
|
copy(data, msg28[len(msg28)-32:])
|
|
return msg
|
|
}
|
|
|
|
//func (c *Conn) msgAck0020(msg28 []byte) []byte {
|
|
// const cmdDataSize = 36
|
|
//
|
|
// msg := c.msg(msgHhrSize + cmdHdrSize + cmdDataSize)
|
|
//
|
|
// cmd := msg[msgHhrSize:]
|
|
// copy(cmd, "\x00\x14\x0b\x00")
|
|
// cmd[16] = cmdDataSize
|
|
// copy(cmd[20:], msg28[20:24]) // request id (random)
|
|
//
|
|
// data := cmd[cmdHdrSize:]
|
|
// data[5] = 1
|
|
// data[7] = 1
|
|
// data[8] = 1
|
|
// data[12] = 4
|
|
// copy(data[16:], "\xfb\x07\x1f\x00")
|
|
// data[30] = 3
|
|
// data[32] = 1
|
|
// return msg
|
|
//}
|
|
|
|
func (c *Conn) msg0012() []byte {
|
|
// -> 00120b000000000000000000000000000c00000000000000020000000100000001000000
|
|
const dataSize = 12
|
|
msg := c.msg(msgHhrSize + cmdHdrSize + dataSize)
|
|
cmd := msg[msgHhrSize:]
|
|
|
|
copy(cmd, "\x00\x12\x0b\x00")
|
|
cmd[16] = dataSize
|
|
data := cmd[cmdHdrSize:]
|
|
|
|
data[0] = 2
|
|
data[4] = 1
|
|
data[9] = 1
|
|
return msg
|
|
}
|
|
|
|
func (c *Conn) msgAck0007(msg28 []byte) []byte {
|
|
// <- 000708000000000000000000000000000c00000001000000000000001c551f7a00000000
|
|
// -> 010a0b00000000000000000000000000000000000100000000000000
|
|
msg := c.msg(msgHhrSize + 28)
|
|
cmd := msg[msgHhrSize:]
|
|
copy(cmd, "\x01\x0a\x0b\x00")
|
|
cmd[20] = 1
|
|
return msg
|
|
}
|
|
|
|
func (c *Conn) msgAck0008(msg28 []byte) []byte {
|
|
// <- 000808000000000000000000000000000000f9f0010000000200000050f31f7a
|
|
// -> 01090b0000000000000000000000000000000000010000000200000050f31f7a
|
|
msg := c.msg(msgHhrSize + 28)
|
|
cmd := msg[msgHhrSize:]
|
|
copy(cmd, "\x01\x09\x0b\x00")
|
|
copy(cmd[20:], msg28[20:])
|
|
return msg
|
|
}
|
|
|
|
func (c *Conn) msgAck0A08(msg28 []byte) []byte {
|
|
// <- 0a080b005b0000000b51590002000000
|
|
// -> 0b000b00000001000b5103000300000000000000
|
|
msg := c.msg(msgHhrSize + 20)
|
|
cmd := msg[msgHhrSize:]
|
|
copy(cmd, "\x0b\x00\x0b\x00")
|
|
copy(cmd[8:], msg28[8:10])
|
|
return msg
|
|
}
|
|
|
|
func msgAckPing(req []byte) []byte {
|
|
// <- [24] 0402120a 08000000 28041200 000000005b0d4202070aa8c0
|
|
// -> [24] 04021a0a 08000000 27042100 000000005b0d4202070aa8c0
|
|
req[8] = 0x27
|
|
req[10] = 0x21
|
|
return req
|
|
}
|