Improve support tutk vendor for xiaomi source

This commit is contained in:
Alex X
2026-01-01 07:49:21 +03:00
parent 44da81774c
commit e77210f916
8 changed files with 682 additions and 276 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ func Dial(rawURL string) (*Client, error) {
case "cs2":
c.conn, err = cs2.Dial(u.Host, query.Get("transport"))
case "tutk":
c.conn, err = tutk.Dial(u.Host, query.Get("uid"))
c.conn, err = tutk.Dial(u.Host, query.Get("uid"), query.Get("model"))
default:
return nil, fmt.Errorf("miss: unsupported vendor %s", s)
}
+2 -2
View File
@@ -75,7 +75,7 @@ func Dial(rawURL string) (core.Producer, error) {
}
func probe(client *miss.Client, channel, quality, audio uint8) ([]*core.Media, error) {
_ = client.SetDeadline(time.Now().Add(core.ProbeTimeout))
_ = client.SetDeadline(time.Now().Add(10 * time.Second))
if err := client.VideoStart(channel, quality, audio&1); err != nil {
return nil, err
@@ -158,7 +158,7 @@ func (p *Producer) Start() error {
var audioTS uint32
for {
_ = p.client.SetDeadline(time.Now().Add(core.ConnDeadline))
_ = p.client.SetDeadline(time.Now().Add(10 * time.Second))
pkt, err := p.client.ReadPacket()
if err != nil {
return err
+63
View File
@@ -0,0 +1,63 @@
# TUTK
The most terrible protocol I have ever had to work with.
## Messages
Ping from camera (24b). The shortest message.
```
off sample
0 0402 tutk magic
2 190a tutk version (120a, 190a...)
4 0800 msg size = len(b)-16 = 24-16
6 0000 channel seq (always 0 for ping)
8 2804 msg type (2804 - ping from camera, 0804 - usual msg from camera)
10 1200 direction (12 - from camera, 21 - from client)
12 00000000 fixed
16 7ecc93c4 random
20 56c2561f random
```
Usual msg from camera (52b + msg data).
```
off sample
12 e6e8 same bytes b[20:22]
14 0000 channel (0, 1, 5)
16 0c00 fixed
18 0000 fixed
20 e6e839da random session id
24 66b0dc14 random session id
28 0070 command
30 0b00 version
32 0100 command seq
34 0000 ???
36 00000000 ???
40 00000000 ???
44 e300 msg data size
46 0000 ???
48 8f15a02f random msg id
52 ... msg data
```
Message with media from camera.
```
off sample
28 0c00 command
30 0b00 version
32 7700 command seq
34 0000 ??? data only for last message per pack (14/14)
36 0200 pack seq, don't know how packs used
38 0914 09/14 - message seq/messages per packs
40 01000000 fixed
42 0500 command 2
44 3200 command 2 seq
46 4f00 chunks count per this frame
48 1b00 chunk seq, starts from 0 (wrong for last chunk)
50 0004 frame data size
52 c8f6 random msg id
54 01000000 previous frame seq, starts from 0
58 02000000 current frame seq, starts from 1
```
+63 -263
View File
@@ -12,23 +12,33 @@ import (
"time"
)
func Dial(host, uid string) (*Conn, error) {
func Dial(host, uid, model string) (*Conn, error) {
conn, err := net.ListenUDP("udp", nil)
if err != nil {
return nil, err
}
c := &Conn{
conn: conn,
addr: &net.UDPAddr{IP: net.ParseIP(host), Port: 32761},
sid: genSID(),
addr, err := net.ResolveUDPAddr("udp", host)
if err != nil {
addr = &net.UDPAddr{IP: net.ParseIP(host), Port: 32761}
}
c := &Conn{conn: conn, addr: addr, sid: genSID()}
if err = c.handshake([]byte(uid)); err != nil {
_ = c.Close()
return nil, err
}
switch model {
case "isa.camera.df3":
c.msgCtrl = c.oldMsgCtrl
c.handleCh0 = c.oldHandlerCh0()
default:
c.msgCtrl = c.newMsgCtrl
c.handleCh0 = c.newHandlerCh0()
}
c.rawCmd = make(chan []byte, 10)
c.rawPkt = make(chan []byte, 100)
@@ -43,20 +53,32 @@ type Conn struct {
sid []byte
err error
seqCh0 uint16
seqCmd uint16
rawCmd chan []byte
rawPkt chan []byte
cmdMu sync.Mutex
cmdAck func()
seqSendCh0 uint16
seqSendCh1 uint16
seqSendCmd1 uint16
seqSendCmd2 uint16
seqSendCnt uint16
seqRecvPkt0 uint16
seqRecvPkt1 uint16
seqRecvCmd2 uint16
msgCtrl func(ctrlType uint16, ctrlData []byte) []byte
handleCh0 func(cmd []byte) int8
}
func (c *Conn) handshake(uid []byte) (err error) {
_ = c.SetDeadline(time.Now().Add(5 * time.Second))
if _, err = c.WriteAndWait(
c.msgLanSearch(uid, 1), // 01062100
c.msgConnectByUID(uid, 1),
func(_, res []byte) bool {
return bytes.Index(res, uid) == 16 // 02061200
},
@@ -64,15 +86,14 @@ func (c *Conn) handshake(uid []byte) (err error) {
return err
}
if err = c.Write(c.msgLanSearch(uid, 2)); err != nil {
if err = c.Write(c.msgConnectByUID(uid, 2)); err != nil {
return err
}
if _, err = c.WriteAndWait(
c.msgAvClientStartReq(), // 07042100 + 00000b00
c.msgAvClientStart(),
func(req, res []byte) bool {
mid := req[48:52]
return bytes.Index(res, mid) == 48 // 08041200 + 00140800
return bytes.Index(res, req[48:52]) == 48
},
); err != nil {
return err
@@ -90,135 +111,45 @@ func (c *Conn) worker() {
}()
buf := make([]byte, 1200)
var waitSeq uint16
var waitSize uint32
var waitData []byte
for {
n, addr, err := c.conn.ReadFromUDP(buf)
n, _, err := c.ReadFromUDP(buf)
if err != nil {
c.err = fmt.Errorf("%s: %w", "tutk", err)
return
}
if c.handleMsg(buf[:n]) <= 0 {
if c.err != nil {
return
}
fmt.Printf("tutk: unknown msg: %x\n", buf[:n])
}
}
}
func (c *Conn) Write(buf []byte) error {
//log.Printf("-> %x", buf)
_, err := c.conn.WriteToUDP(TransCodePartial(nil, buf), c.addr)
return err
}
func (c *Conn) ReadFromUDP(buf []byte) (n int, addr *net.UDPAddr, err error) {
for {
if n, addr, err = c.conn.ReadFromUDP(buf); err != nil {
return 0, nil, err
}
if string(addr.IP) != string(c.addr.IP) || n < 16 {
continue // skip messages from another IP
}
b := ReverseTransCodePartial(buf[:n])
//log.Printf("<- %x", b)
if b[0] != 0x04 || b[1] != 0x02 {
continue
}
if len(b) == 24 {
_ = c.Write(msgAckPing(b))
continue
}
switch b[14] {
case 0:
switch string(b[28:30]) {
case "\x00\x12":
_ = c.Write(c.msgAckCh0Req0012(b))
continue
case "\x00\x70":
_ = c.Write(c.msgAckCh0Req0070(b))
select {
case c.rawCmd <- b[52:]:
default:
}
continue
case "\x00\x71":
if c.cmdAck != nil {
c.cmdAck()
}
continue
case "\x01\x03":
seq := binary.LittleEndian.Uint16(b[40:])
if seq != waitSeq {
waitSeq = 0 // data loss
continue
}
if seq == 0 {
waitSize = binary.LittleEndian.Uint32(b[36:]) + 32
}
waitData = append(waitData, b[52:]...)
if n := uint32(len(waitData)); n < waitSize {
waitSeq++
continue
} else if n > waitSize {
waitSeq = 0 // data loss
continue
}
// create a buffer for the header and collected data
packetData := make([]byte, waitSize)
// there's a header at the end - let's move it to the beginning
copy(packetData, waitData[waitSize-32:])
copy(packetData[32:], waitData)
select {
case c.rawPkt <- packetData:
default:
c.err = fmt.Errorf("%s: media queue is full", "tutk")
return
}
waitSeq = 0
waitData = waitData[:0]
continue
case "\x01\x04":
waitSize2 := binary.LittleEndian.Uint32(b[36:])
waitData2 := b[52:]
if uint32(len(waitData2)) != waitSize2 {
continue // shouldn't happened for audio
}
packetData := make([]byte, waitSize2)
copy(packetData, waitData2)
select {
case c.rawPkt <- packetData:
default:
c.err = fmt.Errorf("%s: media queue is full", "tutk")
return
}
continue
}
case 1:
switch string(b[28:30]) {
case "\x00\x00":
_ = c.Write(msgAckCh1Req0000(b))
continue
case "\x00\x07":
_ = c.Write(msgAckCh1Req0007(b))
continue
}
case 5:
if len(b) == 48 {
_ = c.Write(msgAckCh5(b))
continue
}
}
fmt.Printf("%s: unknown msg: %x\n", "tutk", buf[:n])
ReverseTransCodePartial(buf, buf[:n])
//log.Printf("<- %x", buf[:n])
return n, addr, nil
}
}
func (c *Conn) Write(req []byte) error {
//log.Printf("-> %x", req)
_, err := c.conn.WriteToUDP(TransCodePartial(req), c.addr)
return err
}
func (c *Conn) WriteAndWait(req []byte, ok func(req, res []byte) bool) ([]byte, error) {
var t *time.Timer
t = time.AfterFunc(1, func() {
@@ -231,20 +162,14 @@ func (c *Conn) WriteAndWait(req []byte, ok func(req, res []byte) bool) ([]byte,
buf := make([]byte, 1200)
for {
n, addr, err := c.conn.ReadFromUDP(buf)
n, addr, err := c.ReadFromUDP(buf)
if err != nil {
return nil, err
}
if string(addr.IP) != string(c.addr.IP) || n < 16 {
continue // skip messages from another IP
}
res := ReverseTransCodePartial(buf[:n])
//log.Printf("<- %x", b)
if ok(req, res) {
if ok(req, buf[:n]) {
c.addr.Port = addr.Port
return res, nil
return buf[:n], nil
}
}
}
@@ -298,10 +223,10 @@ func (c *Conn) WriteCommand(cmd uint16, data []byte) error {
timeout.Reset(1)
}
req := c.msgAvSendIOCtrl(cmd, data)
msg := c.msgCtrl(cmd, data)
for {
if err := c.Write(req); err != nil {
if err := c.WriteCh0(msg); err != nil {
return err
}
<-timeout.C
@@ -334,128 +259,3 @@ func genSID() []byte {
b[4] = 0x0c
return b
}
func (c *Conn) msgLanSearch(uid []byte, i byte) []byte {
const size = 68 // or 52 or 68 or 88
b := make([]byte, size)
copy(b, "\x04\x02\x0f\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
return b
}
func (c *Conn) msg(size uint16) []byte {
b := make([]byte, size)
copy(b, "\x04\x02\x19\x0a")
binary.LittleEndian.PutUint16(b[4:], size-16)
binary.LittleEndian.PutUint16(b[6:], c.seqCh0)
c.seqCh0++ // start from 0
copy(b[8:], "\x07\x04\x21\x00")
return b
}
func (c *Conn) msgAvClientStartReq() []byte {
const size = 586 // or 586 or 598
b := c.msg(size)
copy(b[12:], c.sid)
copy(b[28:], "\x00\x00\x08\x00") // or 00000400 or 00000b00
binary.LittleEndian.PutUint16(b[44:], size-52)
binary.LittleEndian.PutUint32(b[48:], uint32(time.Now().UnixMilli()))
copy(b[size-16:], "\x04\x00\x00\x00\xfb\x07\x1f\x00")
return b
}
func (c *Conn) msgAvSendIOCtrl(cmd uint16, msg []byte) []byte {
size := 52 + 4 + uint16(len(msg))
b := c.msg(size)
copy(b[12:], c.sid)
copy(b[28:], "\x00\x70\x08\x00") // or 00700400 or 00700b00
c.seqCmd++ // start from 1
binary.LittleEndian.PutUint16(b[32:], c.seqCmd)
binary.LittleEndian.PutUint16(b[44:], size-52)
//_, _ = rand.Read(b[48:52]) // mid
binary.LittleEndian.PutUint32(b[48:], uint32(time.Now().UnixMilli()))
binary.LittleEndian.PutUint16(b[52:], cmd)
copy(b[56:], msg)
return b
}
const version = 0x19
func msgAckPing(req []byte) []byte {
// <- [24] 0402120a 08000000 28041200 000000005b0d4202070aa8c0
// -> [24] 04021a0a 08000000 27042100 000000005b0d4202070aa8c0
req[2] = version
req[8] = 0x27
req[10] = 0x21
return req
}
func msgAck(req []byte, size byte) []byte {
// xxxx??xx ??00xxxx 07xx21xx ...
req[2] = version
req[4] = size - 16
req[5] = 0x00
req[8] = 0x07
req[10] = 0x21
return req[:size]
}
func (c *Conn) msgAckCh0Req0012(req []byte) []byte {
// <- [64] 0402120a 30000000 08041200 e6e8 0000 0c000000e6e839da66b0dc14 00120800000000000000000000000000 0c00 000000000000 020000000100000001000000
// -> [72] 0402190a 38000300 07042100 e6e8 0000 0c000000e6e839da66b0dc14 00130b00000000000000000000000000 1400 000000000000 0200000001000000010000000000000000000000
const size = 72
req = append(req, 0, 0, 0, 0, 0, 0, 0, 0)
binary.LittleEndian.PutUint16(req[6:], c.seqCh0) // channel sequence
c.seqCh0++
req[28] = 0x00 // command
req[29] = 0x13
req[44] = size - 52 // data size
req[45] = 0x00
return msgAck(req, size)
}
func (c *Conn) msgAckCh0Req0070(req []byte) []byte {
// <- [104] 0402120a 58000300 08041200 e6e8 0000 0c000000e6e839da66b0dc14 00700800010000000000000000000000 3400 00007625a02f ...
// -> [ 52] 0402190a 24000400 07042100 e6e8 0000 0c000000e6e839da66b0dc14 00710800010000000000000000000000 0000 00007625a02f
binary.LittleEndian.PutUint16(req[6:], c.seqCh0) // channel sequence
c.seqCh0++
req[28] = 0x00 // command
req[29] = 0x71
req[44] = 0x00 // data size
req[45] = 0x00
return msgAck(req, 52)
}
func msgAckCh1Req0000(req []byte) []byte {
// <- [590] 0402120a 3e020100 08041200 e6e8 0100 0c000000e6e839da66b0dc14 00000800000000000000000000000000 1a02 0000d9c0001b ...
// -> [ 84] 0402190a 44000000 07042100 e6e8 0100 0c000000e6e839da66b0dc14 00140b00000000000000000000000000 2000 0000d9c0001b ...
const size = 84
req[28] = 0x00 // command
req[29] = 0x14
req[44] = size - 52 // data size
req[45] = 0x00
copy(req[52:], req[len(req)-32:]) // size
return msgAck(req, size)
}
func msgAckCh1Req0007(req []byte) []byte {
// <- [64] 0402120a 30000300 08041200 e6e8 0100 0c000000e6e839da66b0dc14 00070800000000000000000000000000 0c00 000001000000 000000006f1ea02f00000000
// -> [56] 0402190a 28000200 07042100 e6e8 0100 0c000000e6e839da66b0dc14 010a0b00000000000000000000000000 0000 000001000000 00000000
req[28] = 0x01 // command
req[29] = 0x0a
req[44] = 0x00 // data size
req[45] = 0x00
return msgAck(req, 56)
}
func msgAckCh5(req []byte) []byte {
// <- [48] 0402120a 20000200 08041200 e6e8 0500 0c000000e6e839da66b0dc14 5a97c2f1010500000000000000000000 00a0 0000
// -> [48] 0402190a 20000200 07042100 e6e8 0500 0c000000e6e839da66b0dc14 5a97c2f1410500000000000000000000 00a0 0000
req[32] = 0x41
return msgAck(req, 48)
}
+13 -10
View File
@@ -1,7 +1,6 @@
package tutk
import (
"bytes"
"encoding/binary"
"math/bits"
)
@@ -9,10 +8,12 @@ import (
// I'd like to say hello to Charlie. Your name is forever etched into the history of streaming software.
const charlie = "Charlie is the designer of P2P!!"
func ReverseTransCodePartial(src []byte) []byte {
func ReverseTransCodePartial(dst, src []byte) []byte {
n := len(src)
tmp := make([]byte, n)
dst := bytes.Clone(src)
if len(dst) < n {
dst = make([]byte, n)
}
src16 := src
tmp16 := tmp
@@ -24,7 +25,7 @@ func ReverseTransCodePartial(src []byte) []byte {
binary.LittleEndian.PutUint32(tmp16[i:], bits.RotateLeft32(x, i+3))
}
swap(tmp16, dst16, 16)
swap(dst16, tmp16, 16)
for i := 0; i != 16; i++ {
tmp16[i] = dst16[i] ^ charlie[i]
@@ -40,7 +41,7 @@ func ReverseTransCodePartial(src []byte) []byte {
src16 = src16[16:]
}
swap(src16, tmp16, n)
swap(tmp16, src16, n)
for i := 0; i < n; i++ {
dst16[i] = tmp16[i] ^ charlie[i]
@@ -49,10 +50,12 @@ func ReverseTransCodePartial(src []byte) []byte {
return dst
}
func TransCodePartial(src []byte) []byte {
func TransCodePartial(dst, src []byte) []byte {
n := len(src)
tmp := make([]byte, n)
dst := bytes.Clone(src)
if len(dst) < n {
dst = make([]byte, n)
}
src16 := src
tmp16 := tmp
@@ -68,7 +71,7 @@ func TransCodePartial(src []byte) []byte {
dst16[i] = tmp16[i] ^ charlie[i]
}
swap(dst16, tmp16, 16)
swap(tmp16, dst16, 16)
for i := 0; i != 16; i += 4 {
x := binary.LittleEndian.Uint32(tmp16[i:])
@@ -84,12 +87,12 @@ func TransCodePartial(src []byte) []byte {
tmp16[i] = src16[i] ^ charlie[i]
}
swap(tmp16, dst16, n)
swap(dst16, tmp16, n)
return dst
}
func swap(src, dst []byte, n int) {
func swap(dst, src []byte, n int) {
switch n {
case 2:
_, _ = src[1], dst[1]
+196
View File
@@ -0,0 +1,196 @@
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
}
+191
View File
@@ -0,0 +1,191 @@
package tutk
import (
"encoding/binary"
"fmt"
"time"
)
func (c *Conn) newMsgCtrl(ctrlType uint16, ctrlData []byte) []byte {
size := msgHhrSize + 28 + 4 + uint16(len(ctrlData))
msg := c.msg(size)
// 0 0070 command
// 2 0b00 version
// 4 1000 seq
// 6 0076 ???
cmd := msg[msgHhrSize:]
copy(cmd, "\x00\x70\x0b\x00")
binary.LittleEndian.PutUint16(cmd[4:], c.seqSendCmd1)
c.seqSendCmd1++
// 8 0070 command (second time)
// 10 0300 seq
// 12 0100 chunks count
// 14 0000 chunk seq (starts from 0)
// 16 5500 size
// 18 0000 random msg id (always 0)
// 20 03000000 seq (second time)
// 24 00000000
// 28 01010000 ctrlType
cmd[9] = 0x70
cmd[12] = 1
binary.LittleEndian.PutUint16(cmd[16:], size-52)
binary.LittleEndian.PutUint16(cmd[10:], c.seqSendCmd2)
binary.LittleEndian.PutUint16(cmd[20:], c.seqSendCmd2)
c.seqSendCmd2++
data := cmd[28:]
binary.LittleEndian.PutUint16(data, ctrlType)
copy(data[4:], ctrlData)
return msg
}
func (c *Conn) newHandlerCh0() func(msg []byte) int8 {
var waitData []byte
var waitSeq uint16
return func(cmd []byte) int8 {
switch cmd[0] {
case 0x07, 0x05:
flag := cmd[1]
var cmd2 []byte
if flag&0b1000 == 0 {
// off sample
// 0 0700 command
// 2 0b00 version
// 4 2700 seq
// 6 0000 ???
// 8 0700 command (second time)
// 10 1400 seq
// 12 1300 chunks count per this frame
// 14 1100 chunk seq, starts from 0 (0x20 for last chunk)
// 16 0004 frame data size
// 18 0000 random msg id (always 0)
// 20 02000000 previous frame seq, starts from 0
// 24 03000000 current frame seq, starts from 1
cmd2 = cmd[8:]
} else {
// off sample
// 0 070d0b00
// 4 30000000
// 8 5c965500 ???
// 12 ffff0000 ???
// 16 0701 fixed command
// 18 190001002000a802000006000000070000000
cmd2 = cmd[16:]
}
seq := binary.LittleEndian.Uint16(cmd2[2:])
// Check if this is first chunk for frame.
// Handle protocol bug "0x20 chunk seq for last chunk" and sometimes
// "0x20 chunk seq for first chunk if only one chunk".
if binary.LittleEndian.Uint16(cmd2[6:]) == 0 || binary.LittleEndian.Uint16(cmd2[4:]) == 1 {
waitData = waitData[:0]
waitSeq = seq
} else if seq != waitSeq {
return msgMediaLost
}
if flag&0b0001 == 0 {
waitData = append(waitData, cmd2[20:]...)
waitSeq++
return msgMediaChunk
}
c.seqRecvPkt1 = seq
_ = c.WriteCh0(c.msgAckCounters())
data := cmd2[20:]
n := len(data) - 32
waitData = append(waitData, data[:n]...)
packetData := make([]byte, 32+len(waitData))
copy(packetData, data[n:])
copy(packetData[32:], waitData)
select {
case c.rawPkt <- packetData:
default:
c.err = fmt.Errorf("%s: media queue is full", "tutk")
return -1
}
return msgMediaFrame
case 0x00:
_ = c.WriteCh0(c.msgAckCounters())
c.seqRecvCmd2 = binary.LittleEndian.Uint16(cmd[2:])
switch cmd[1] {
case 0x10:
return msgUnknown0010 // unknown
case 0x70:
select {
case c.rawCmd <- cmd[28:]:
default:
}
return msgCommand // cmd from camera
}
case 0x09:
// off sample
// 0 09000b00 cmd1
// 4 0d000000 seqCmd1
// 12 0000 seqRecvCmd2
seq := binary.LittleEndian.Uint16(cmd[12:])
if c.seqSendCmd1 > seq {
if c.cmdAck != nil {
c.cmdAck()
}
}
return msgCounters
case 0x0a:
// seq sample
// 0 0a080b00
// 4 03000000
// 8 e2043200
// 12 01000000
_ = c.WriteCh0(c.msgAck0A08(cmd))
return msgUnknown0a08
}
return 0
}
}
func (c *Conn) msgAckCounters() []byte {
msg := c.msg(msgHhrSize + cmdHdrSize)
// off sample
// 0 09000b00 cmd1
// 4 2700 seqCmd1
// 6 0000
// 8 1300 seqRecvPkt0
// 10 2600 seqRecvPkt1
// 12 0400 seqRecvCmd2
// 14 00000000
// 18 1400 seqSendCnt
// 20 d91a random
// 22 0000
cmd := msg[msgHhrSize:]
copy(cmd, "\x09\x00\x0b\x00")
binary.LittleEndian.PutUint16(cmd[4:], c.seqSendCmd1)
c.seqSendCmd1++
// seqRecvPkt0 stores previous value of seqRecvPkt1
// don't understand why this needs
binary.LittleEndian.PutUint16(cmd[8:], c.seqRecvPkt0)
c.seqRecvPkt0 = c.seqRecvPkt1
binary.LittleEndian.PutUint16(cmd[10:], c.seqRecvPkt1)
binary.LittleEndian.PutUint16(cmd[12:], c.seqRecvCmd2)
binary.LittleEndian.PutUint16(cmd[18:], c.seqSendCnt)
c.seqSendCnt++
binary.LittleEndian.PutUint16(cmd[20:], uint16(time.Now().UnixNano()))
return msg
}
+153
View File
@@ -0,0 +1,153 @@
package tutk
import (
"encoding/binary"
"fmt"
)
func (c *Conn) oldMsgCtrl(ctrlType uint16, ctrlData []byte) []byte {
size := msgHhrSize + cmdHdrSize + 4 + uint16(len(ctrlData))
msg := c.msg(size)
cmd := msg[msgHhrSize:]
copy(cmd, "\x00\x70\x0b\x00")
binary.LittleEndian.PutUint16(cmd[4:], c.seqSendCmd1)
c.seqSendCmd1++
binary.LittleEndian.PutUint16(cmd[16:], size-52)
//binary.LittleEndian.PutUint32(cmd[20:], uint32(time.Now().UnixMilli()))
data := cmd[cmdHdrSize:]
binary.LittleEndian.PutUint16(data, ctrlType)
copy(data[4:], ctrlData)
return msg
}
func (c *Conn) oldHandlerCh0() func([]byte) int8 {
var waitSeq uint16
var waitSize uint32
var waitData []byte
return func(cmd []byte) int8 {
// 0 01030800 command + version
// 4 00000000 fixed
// 8 ac880100 total size
// 12 6200 chunk seq
// 14 2000 ???
// 16 cc00 size
// 18 0000
// 20 01000000 fixed
switch cmd[0] {
case 0x01:
switch cmd[1] {
case 0x03:
seq := binary.LittleEndian.Uint16(cmd[12:])
if seq != waitSeq {
waitSeq = 0
return msgMediaLost
}
if seq == 0 {
waitData = waitData[:0]
waitSize = binary.LittleEndian.Uint32(cmd[8:]) + 32
}
waitData = append(waitData, cmd[24:]...)
if n := uint32(len(waitData)); n < waitSize {
waitSeq++
return msgMediaChunk
} else if n > waitSize {
waitSeq = 0
return msgMediaLost
}
// create a buffer for the header and collected data
packetData := make([]byte, waitSize)
// there's a header at the end - let's move it to the beginning
copy(packetData, waitData[waitSize-32:])
copy(packetData[32:], waitData)
select {
case c.rawPkt <- packetData:
default:
c.err = fmt.Errorf("%s: media queue is full", "tutk")
return -1
}
waitSeq = 0
return msgMediaFrame
case 0x04:
waitSize2 := binary.LittleEndian.Uint32(cmd[8:])
waitData2 := cmd[24:]
if uint32(len(waitData2)) != waitSize2 {
return -1 // shouldn't happened for audio
}
packetData := make([]byte, waitSize2)
copy(packetData, waitData2)
select {
case c.rawPkt <- packetData:
default:
c.err = fmt.Errorf("%s: media queue is full", "tutk")
return -1
}
return msgMediaFrame
}
case 0x00:
switch cmd[1] {
case 0x70:
_ = c.WriteCh0(c.msgAck0070(cmd))
select {
case c.rawCmd <- cmd[24:]:
default:
}
return msgCommand
case 0x12:
_ = c.WriteCh0(c.msgAck0012(cmd))
return msgDafang0012
case 0x71:
if c.cmdAck != nil {
c.cmdAck()
}
return msgDafang0071
}
}
return 0
}
}
func (c *Conn) msgAck0070(msg28 []byte) []byte {
// <- [104] 0402120a 58000300 08041200 e6e8 0000 0c000000e6e839da66b0dc14 00700800010000000000000000000000 3400 00007625a02f ...
// -> [ 52] 0402190a 24000400 07042100 e6e8 0000 0c000000e6e839da66b0dc14 00710800010000000000000000000000 0000 00007625a02f
msg := c.msg(52)
cmd := msg[msgHhrSize:]
copy(cmd, "\x00\x71\x0b\x00")
binary.LittleEndian.PutUint16(cmd[4:], c.seqSendCmd1)
c.seqSendCmd1++
copy(cmd[8:], msg28[8:10])
return msg
}
func (c *Conn) msgAck0012(msg28 []byte) []byte {
// <- [64] 0402120a 30000000 08041200 e6e800000c000000e6e839da66b0dc14 001208000000000000000000000000000c00000000000000 020000000100000001000000
// -> [72] 0402190a 38000300 07042100 e6e800000c000000e6e839da66b0dc14 00130b000000000000000000000000001400000000000000 0200000001000000010000000000000000000000
const size = 72
msg := c.msg(size)
cmd := msg[msgHhrSize:]
copy(cmd, "\x00\x13\x0b\x00")
cmd[16] = size - 52 // data size
data := cmd[cmdHdrSize:]
copy(data, msg28[cmdHdrSize:])
return msg
}