install go2rtc on bob
This commit is contained in:
@@ -0,0 +1,337 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user