Files
go2rtc/pkg/xiaomi/tutk/proto.go
T
2026-01-01 09:28:02 +03:00

197 lines
4.4 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
msgUnknown0010
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 {
switch cid := string(cmd[:2]); cid {
case "\x00\x00":
_ = c.WriteCh1(c.msgAck0000(cmd))
return msgClientStart00
case "\x00\x20":
//_ = c.WriteCh1(c.msgAck0020(cmd))
return msgClientStart20
case "\x09\x00": // skip
return msgCounters
case "\x0a\x08":
_ = 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 {
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)
// It's better not to answer anything, so camera won't send anything to this channel.
//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) msgAck0A08(msg28 []byte) []byte {
msg := c.msg(48)
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
}