package tutk import ( "bytes" "encoding/binary" "net" "time" ) func NewSession25(conn net.Conn, sid []byte) *Session25 { return &Session25{ Session16: NewSession16(conn, sid), rb: NewReorderBuffer(5), } } type Session25 struct { *Session16 rb *ReorderBuffer seqSendCmd2 uint16 seqSendCnt uint16 seqRecvPkt0 uint16 seqRecvPkt1 uint16 seqRecvCmd2 uint16 } const cmdHdrSize25 = 28 func (s *Session25) SendIOCtrl(ctrlType uint32, ctrlData []byte) []byte { size := msgHhrSize + cmdHdrSize25 + 4 + uint16(len(ctrlData)) msg := s.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:], s.seqSendCmd1) s.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:], s.seqSendCmd2) binary.LittleEndian.PutUint16(cmd[20:], s.seqSendCmd2) s.seqSendCmd2++ data := cmd[28:] binary.LittleEndian.PutUint32(data, ctrlType) copy(data[4:], ctrlData) return msg } func (s *Session25) SendFrameData(frameInfo, frameData []byte) []byte { return nil } func (s *Session25) SessionRead(chID byte, cmd []byte) (res int) { if chID != 0 { return s.handleCh1(cmd) } switch cmd[0] { case 0x03, 0x05, 0x07: for i := 0; cmd != nil; i++ { res = s.handleChunk(cmd, i == 0) cmd = s.rb.Pop() } return case 0x00: _ = s.SessionWrite(0, s.msgAckCounters()) s.seqRecvCmd2 = binary.LittleEndian.Uint16(cmd[2:]) switch cmd[1] { case 0x10: return msgUnknown0010 // unknown case 0x21: return msgClientStartAck2 case 0x70: select { case s.rawCmd <- cmd[28:]: default: } return msgCommand // cmd from camera case 0x71: return msgCommandAck } case 0x09: // off sample // 0 09000b00 cmd1 // 4 0d000000 seqCmd1 // 12 0000 seqRecvCmd2 seq := binary.LittleEndian.Uint16(cmd[12:]) if s.seqSendCmd1 > seq { return msgCommandAck } return msgCounters case 0x0a: // seq sample // 0 0a080b00 // 4 03000000 // 8 e2043200 // 12 01000000 _ = s.SessionWrite(0, s.msgAck0A08(cmd)) return msgUnknown0a08 } return msgUnknown } func (s *Session25) handleChunk(cmd []byte, checkSeq bool) int { var cmd2 []byte flags := cmd[1] if flags&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:]) if checkSeq { if s.rb.Check(seq) { s.rb.Next() } else { s.rb.Push(seq, cmd) return msgMediaReorder } } // 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 { s.waitData = s.waitData[:0] s.waitCSeq = seq } else if seq != s.waitCSeq { return msgMediaLost } s.waitData = append(s.waitData, cmd2[20:]...) if flags&0b0001 == 0 { s.waitCSeq++ return msgMediaChunk } s.seqRecvPkt1 = seq _ = s.SessionWrite(0, s.msgAckCounters()) n := len(s.waitData) - 32 packetData := [2][]byte{bytes.Clone(s.waitData[n:]), bytes.Clone(s.waitData[:n])} select { case s.rawPkt <- packetData: default: return msgError } return msgMediaFrame } func (s *Session25) msgAckCounters() []byte { msg := s.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:], s.seqSendCmd1) s.seqSendCmd1++ // seqRecvPkt0 stores previous value of seqRecvPkt1 // don't understand why this needs binary.LittleEndian.PutUint16(cmd[8:], s.seqRecvPkt0) s.seqRecvPkt0 = s.seqRecvPkt1 binary.LittleEndian.PutUint16(cmd[10:], s.seqRecvPkt1) binary.LittleEndian.PutUint16(cmd[12:], s.seqRecvCmd2) binary.LittleEndian.PutUint16(cmd[18:], s.seqSendCnt) s.seqSendCnt++ binary.LittleEndian.PutUint16(cmd[20:], uint16(time.Now().UnixMilli())) return msg } func (s *Session25) handleCh1(cmd []byte) int { switch cid := string(cmd[:2]); cid { case "\x00\x00": // client start return msgClientStart case "\x00\x07": // time sync without data _ = s.SessionWrite(1, s.msgAck0007(cmd)) return msgUnknown0007 case "\x00\x20": // client start2 _ = s.SessionWrite(1, s.msgAck0020(cmd)) return msgClientStart2 case "\x09\x00": return msgUnknown0900 case "\x0a\x08": return msgUnknown0a08 } return msgUnknown } func (s *Session25) msgAck0020(msg28 []byte) []byte { const cmdDataSize = 36 msg := s.Msg(msgHhrSize + cmdHdrSize25 + cmdDataSize) cmd := msg[msgHhrSize:] copy(cmd, "\x00\x21\x0b\x00") cmd[16] = cmdDataSize copy(cmd[20:], msg28[20:24]) // request id (random) // 0 00000000 // 4 00010001 // 8 01000000 // 12 04000000 // 16 fb071f00 // 20 00000000 // 24 00000000 // 28 00000300 // 32 01000000 data := cmd[cmdHdrSize25:] 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 (s *Session25) msgAck0A08(msg28 []byte) []byte { // <- 0a080b005b0000000b51590002000000 // -> 0b000b00000001000b5103000300000000000000 msg := s.Msg(msgHhrSize + 20) cmd := msg[msgHhrSize:] copy(cmd, "\x0b\x00\x0b\x00") copy(cmd[8:], msg28[8:10]) return msg } // ReorderBuffer used for UDP incoming data. Because the order of the packets may be mixed up. type ReorderBuffer struct { buf map[uint16][]byte seq uint16 size int } func NewReorderBuffer(size int) *ReorderBuffer { return &ReorderBuffer{buf: make(map[uint16][]byte), size: size} } // Check return OK if this is the seq we are waiting for. func (r *ReorderBuffer) Check(seq uint16) (ok bool) { return seq == r.seq } func (r *ReorderBuffer) Next() { r.seq++ } // Available return how much free slots is in the buffer. func (r *ReorderBuffer) Available() int { return r.size - len(r.buf) } // Push new item to buffer. Important! There is no buffer full check here. func (r *ReorderBuffer) Push(seq uint16, data []byte) { //log.Printf("push seq=%d wait=%d", seq, r.seq) r.buf[seq] = bytes.Clone(data) } // Pop latest item from buffer. OK - if items wasn't dropped. func (r *ReorderBuffer) Pop() []byte { for { if data := r.buf[r.seq]; data != nil { delete(r.buf, r.seq) r.Next() //log.Printf("pop seq=%d", r.seq) return data } if r.Available() > 0 { return nil } //log.Printf("drop seq=%d", r.seq) r.Next() // drop item } }