This commit is contained in:
seydx
2026-01-17 22:12:36 +01:00
parent c493087876
commit b220959e41
3 changed files with 103 additions and 81 deletions
+92 -79
View File
@@ -232,19 +232,60 @@ func (c *DTLSConn) AVServStart() error {
return fmt.Errorf("dtls: server handshake failed: %w", err) return fmt.Errorf("dtls: server handshake failed: %w", err)
} }
if c.verbose {
fmt.Printf("[DTLS] Server handshake complete on channel %d\n", iotcChannelBack)
fmt.Printf("[SERVER] Waiting for AV Login request from camera...\n")
}
// Wait for AV Login request from camera
buf := make([]byte, 1024)
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
n, err := conn.Read(buf)
if err != nil {
go conn.Close()
return fmt.Errorf("read av login: %w", err)
}
if c.verbose {
fmt.Printf("[SERVER] AV Login request len=%d data:\n%s", n, hexDump(buf[:n]))
}
if n < 24 {
go conn.Close()
return fmt.Errorf("av login too short: %d bytes", n)
}
checksum := binary.LittleEndian.Uint32(buf[20:])
resp := c.msgAVLoginResponse(checksum)
if c.verbose {
fmt.Printf("[SERVER] Sending AV Login response: %d bytes\n", len(resp))
}
if _, err = conn.Write(resp); err != nil {
go conn.Close()
return fmt.Errorf("write av login response: %w", err)
}
// Camera may resend, respond again
conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
if n, _ = conn.Read(buf); n > 0 {
if c.verbose {
fmt.Printf("[SERVER] Received AV Login resend: %d bytes\n", n)
}
conn.Write(resp)
}
conn.SetReadDeadline(time.Time{})
if c.verbose {
fmt.Printf("[SERVER] AV Login complete, ready for two way streaming\n")
}
c.mu.Lock() c.mu.Lock()
c.serverConn = conn c.serverConn = conn
c.mu.Unlock() c.mu.Unlock()
if c.verbose {
fmt.Printf("[DTLS] Server handshake complete on channel %d\n", iotcChannelBack)
}
// Wait for and respond to AV Login request from camera
if err := c.handleSpeakerAVLogin(); err != nil {
return fmt.Errorf("speaker av login failed: %w", err)
}
return nil return nil
} }
@@ -284,7 +325,7 @@ func (c *DTLSConn) AVSendAudioData(codec byte, payload []byte, timestampUS uint3
conn := c.serverConn conn := c.serverConn
if conn == nil { if conn == nil {
c.mu.Unlock() c.mu.Unlock()
return fmt.Errorf("speaker channel not connected") return fmt.Errorf("av server not ready")
} }
frame := c.msgAudioFrame(payload, timestampUS, codec, sampleRate, channels) frame := c.msgAudioFrame(payload, timestampUS, codec, sampleRate, channels)
@@ -294,9 +335,9 @@ func (c *DTLSConn) AVSendAudioData(codec byte, payload []byte, timestampUS uint3
n, err := conn.Write(frame) n, err := conn.Write(frame)
if c.verbose { if c.verbose {
if err != nil { if err != nil {
fmt.Printf("[SPEAKER TX] DTLS Write ERROR: %v\n", err) fmt.Printf("[SERVER TX] DTLS Write ERROR: %v\n", err)
} else { } else {
fmt.Printf("[SPEAKER TX] len=%d, data:\n%s", n, hexDump(frame)) fmt.Printf("[SERVER TX] len=%d, data:\n%s", n, hexDump(frame))
} }
} }
return err return err
@@ -322,6 +363,11 @@ func (c *DTLSConn) WriteDTLS(payload []byte, channel byte) error {
return c.Write(frame) return c.Write(frame)
} }
func (c *DTLSConn) WriteIOCtrl(payload []byte) error {
_, err := c.conn.Write(c.msgIOCtrl(payload))
return err
}
func (c *DTLSConn) WriteAndWait(req []byte, ok func(res []byte) bool) ([]byte, error) { func (c *DTLSConn) WriteAndWait(req []byte, ok func(res []byte) bool) ([]byte, error) {
var t *time.Timer var t *time.Timer
t = time.AfterFunc(1, func() { t = time.AfterFunc(1, func() {
@@ -386,8 +432,14 @@ func (c *DTLSConn) WriteAndWaitIOCtrl(cmd uint16, payload []byte, expectCmd uint
ack := c.msgACK() ack := c.msgACK()
c.clientConn.Write(ack) c.clientConn.Write(ack)
if len(data) >= 6 { if gotCmd, payload, ok := ParseHL(data); ok {
if binary.LittleEndian.Uint16(data[4:]) == expectCmd { if c.verbose {
fmt.Printf("[DTLS RX] Got rawCmd K%d, expecting K%d, payload=%d bytes\n", gotCmd, expectCmd, len(payload))
if gotCmd != expectCmd && len(payload) > 0 {
fmt.Printf("[DTLS RX] K%d payload:\n%s", gotCmd, hexDump(payload))
}
}
if gotCmd == expectCmd {
return data, nil return data, nil
} }
} }
@@ -423,9 +475,13 @@ func (c *DTLSConn) Close() error {
c.cancel() c.cancel()
c.mu.Lock() c.mu.Lock()
if c.clientConn != nil { if conn := c.serverConn; conn != nil {
c.clientConn.Close() c.serverConn = nil
go conn.Close()
}
if conn := c.clientConn; conn != nil {
c.clientConn = nil c.clientConn = nil
go conn.Close()
} }
if c.frames != nil { if c.frames != nil {
c.frames.Close() c.frames.Close()
@@ -554,27 +610,33 @@ func (c *DTLSConn) worker() {
data := buf[:n] data := buf[:n]
magic := binary.LittleEndian.Uint16(data) magic := binary.LittleEndian.Uint16(data)
if c.verbose {
fmt.Printf("[DTLS RX] magic=0x%04x len=%d\n", magic, n)
}
switch magic { switch magic {
case magicAVLoginResp: case magicAVLoginResp:
c.queue(c.rawCmd, data) c.queue(c.rawCmd, data)
case magicIOCtrl: case magicIOCtrl:
if len(data) >= 32 { if hlData := FindHL(data, 32); hlData != nil {
for i := 32; i+2 < len(data); i++ { if c.verbose {
if data[i] == 'H' && data[i+1] == 'L' { if cmd, _, ok := ParseHL(hlData); ok {
c.queue(c.rawCmd, data[i:]) fmt.Printf("[DTLS RX] IOCtrl HL command K%d\n", cmd)
break
} }
} }
c.queue(c.rawCmd, hlData)
} }
case magicChannelMsg: case magicChannelMsg:
if len(data) >= 36 && data[16] == 0x00 { if len(data) >= 36 && data[16] == 0x00 {
for i := 36; i+2 < len(data); i++ { if hlData := FindHL(data, 36); hlData != nil {
if data[i] == 'H' && data[i+1] == 'L' { if c.verbose {
c.queue(c.rawCmd, data[i:]) if cmd, _, ok := ParseHL(hlData); ok {
break fmt.Printf("[DTLS RX] ChannelMsg HL command K%d\n", cmd)
}
} }
c.queue(c.rawCmd, hlData)
} }
} }
@@ -591,13 +653,13 @@ func (c *DTLSConn) worker() {
} }
// Check for HL command response // Check for HL command response
if len(data) >= 36 { if hlData := FindHL(data, 32); hlData != nil {
for i := 32; i+2 < len(data); i++ { if c.verbose {
if data[i] == 'H' && data[i+1] == 'L' { if cmd, _, ok := ParseHL(hlData); ok {
c.queue(c.rawCmd, data[i:]) fmt.Printf("[DTLS RX] ProtoVersion HL command K%d\n", cmd)
break
} }
} }
c.queue(c.rawCmd, hlData)
} }
} }
@@ -711,55 +773,6 @@ func (c *DTLSConn) queue(ch chan []byte, data []byte) {
} }
} }
func (c *DTLSConn) handleSpeakerAVLogin() error {
if c.verbose {
fmt.Printf("[SPEAK] Waiting for AV Login request from camera...\n")
}
buf := make([]byte, 1024)
c.serverConn.SetReadDeadline(time.Now().Add(2 * time.Second))
n, err := c.serverConn.Read(buf)
if err != nil {
return fmt.Errorf("read av login: %w", err)
}
if c.verbose {
fmt.Printf("[SPEAK] AV Login request len=%d data:\n%s", n, hexDump(buf[:n]))
}
if n < 24 {
return fmt.Errorf("av login too short: %d bytes", n)
}
checksum := binary.LittleEndian.Uint32(buf[20:])
resp := c.msgAVLoginResponse(checksum)
if c.verbose {
fmt.Printf("[SPEAK] Sending AV Login response: %d bytes\n", len(resp))
}
if _, err = c.serverConn.Write(resp); err != nil {
return fmt.Errorf("write AV login response: %w", err)
}
// Camera may resend, respond again
c.serverConn.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
if n, _ = c.serverConn.Read(buf); n > 0 {
if c.verbose {
fmt.Printf("[SPEAK] Received AV Login resend: %d bytes\n", n)
}
c.serverConn.Write(resp)
}
c.serverConn.SetReadDeadline(time.Time{})
if c.verbose {
fmt.Printf("[SPEAK] AV Login complete, ready for audio\n")
}
return nil
}
func (c *DTLSConn) msgDisco(stage byte) []byte { func (c *DTLSConn) msgDisco(stage byte) []byte {
b := make([]byte, discoSize) b := make([]byte, discoSize)
copy(b, "\x04\x02\x1a\x02") // marker + mode copy(b, "\x04\x02\x1a\x02") // marker + mode
+9
View File
@@ -60,3 +60,12 @@ func ParseHL(data []byte) (cmdID uint16, payload []byte, ok bool) {
} }
return cmdID, payload, true return cmdID, payload, true
} }
func FindHL(data []byte, offset int) []byte {
for i := offset; i+16 <= len(data); i++ {
if data[i] == 'H' && data[i+1] == 'L' {
return data[i:]
}
}
return nil
}
+2 -2
View File
@@ -211,7 +211,7 @@ func (c *Client) StartIntercom() error {
k10010 := c.buildK10010(MediaTypeReturnAudio, true) k10010 := c.buildK10010(MediaTypeReturnAudio, true)
if _, err := c.conn.WriteAndWaitIOCtrl(KCmdControlChannel, k10010, KCmdControlChannelResp, 5*time.Second); err != nil { if _, err := c.conn.WriteAndWaitIOCtrl(KCmdControlChannel, k10010, KCmdControlChannelResp, 5*time.Second); err != nil {
return err return fmt.Errorf("enable return audio: %w", err)
} }
return c.conn.AVServStart() return c.conn.AVServStart()
@@ -223,7 +223,7 @@ func (c *Client) StopIntercom() error {
} }
k10010 := c.buildK10010(MediaTypeReturnAudio, false) k10010 := c.buildK10010(MediaTypeReturnAudio, false)
c.conn.WriteAndWaitIOCtrl(KCmdControlChannel, k10010, KCmdControlChannelResp, 5*time.Second) c.conn.WriteIOCtrl(k10010)
return c.conn.AVServStop() return c.conn.AVServStop()
} }