mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2026-04-22 15:47:06 +08:00
338 lines
7.5 KiB
Go
338 lines
7.5 KiB
Go
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
|
|
}
|
|
}
|