minor improvements

This commit is contained in:
seydx
2026-01-17 12:09:38 +01:00
parent 8e6b8b32d6
commit c03cd9f156
5 changed files with 141 additions and 52 deletions
+18 -1
View File
@@ -211,7 +211,7 @@ func (c *Client) StartAudio() error {
}
func (c *Client) StartIntercom() error {
if c.conn.IsBackchannelReady() {
if c.conn == nil || !c.conn.IsBackchannelReady() {
return nil
}
@@ -223,6 +223,17 @@ func (c *Client) StartIntercom() error {
return c.conn.AVServStart()
}
func (c *Client) StopIntercom() error {
if c.conn == nil || !c.conn.IsBackchannelReady() {
return nil
}
k10010 := c.buildK10010(MediaTypeReturnAudio, false)
c.conn.WriteAndWaitIOCtrl(KCmdControlChannel, k10010, KCmdControlChannelResp, 5*time.Second)
return c.conn.AVServStop()
}
func (c *Client) ReadPacket() (*tutk.Packet, error) {
return c.conn.AVRecvFrameData()
}
@@ -270,10 +281,16 @@ func (c *Client) Close() error {
fmt.Printf("[Wyze] Closing connection\n")
}
c.StopIntercom()
if c.conn != nil {
c.conn.Close()
}
if c.verbose {
fmt.Printf("[Wyze] Connection closed\n")
}
return nil
}
+35 -33
View File
@@ -443,38 +443,10 @@ Offset Size Field Description
[31] 1 TwoWayAudio 0x01 if intercom supported
[32-35] 4 Reserved
[36-39] 4 BufferConfig 0x00000004
[40-43] 4 Capabilities 0x001F07FB (see below)
[40-43] 4 Capabilities 0x001F07FB
[44-57] 14 Reserved
```
### Capabilities Bitmask (0x001F07FB)
```
Bit Hex Name Description
──────────────────────────────────────────────────────────────
0 0x00000001 CYCLIC_FRAME_NUMBERING Frame numbers wrap around
1 0x00000002 CLEAN_BUF_ON_RESET Clear buffer on stream reset
3 0x00000008 TIMESTAMP_IN_FRAMEINFO Timestamps in FRAMEINFO struct
4 0x00000010 MULTI_CHANNEL Multiple AV channels supported
5 0x00000020 EXTENDED_FRAMEINFO 40-byte FRAMEINFO (vs 16-byte SDK)
6 0x00000040 RESEND_TIMEOUT Packet resend with timeout
7 0x00000080 DTLS_SUPPORT DTLS encryption supported
8 0x00000100 SPEAKER_CHANNEL Two-way audio / intercom
9 0x00000200 PTZ_CHANNEL PTZ control channel
10 0x00000400 PLAYBACK_CHANNEL SD card playback channel
16 0x00010000 AV_SECURITY_ENABLED Encrypted AV stream
17 0x00020000 RESEND_ENABLED Packet resend mechanism
18 0x00040000 DTLS_PSK DTLS with Pre-Shared Key
19 0x00080000 DTLS_ECDHE DTLS with ECDHE key exchange
20 0x00100000 CHACHA20_POLY1305 ChaCha20-Poly1305 cipher support
```
**0x001F07FB breakdown:**
```
0x001F07FB = 0b0000_0000_0001_1111_0000_0111_1111_1011
= Bits: 0,1,3,4,5,6,7,8,9,10,16,17,18,19,20
```
---
## 7. K-Command Authentication
@@ -515,9 +487,21 @@ Offset Size Field Description
[16+] var Payload Command-specific data
```
### K10000 - Auth Request (16 bytes)
### K10000 - Auth Request (16 + JSON bytes)
Header only, no payload. Initiates authentication.
```
Offset Size Field Description
──────────────────────────────────────────────────────────────
[0-15] 16 HLHeader CommandID = 10000, PayloadLen = len(JSON)
[16+] var JSONPayload Audio codec preferences
```
**JSON Payload:**
```json
{"cameraInfo":{"audioEncoderList":[137,138,140]}}
```
Where audioEncoderList contains supported codec IDs: 137=PCMU, 138=PCMA, 140=PCM.
### K10001 - Challenge (33+ bytes)
@@ -543,7 +527,7 @@ Offset Size Field Description
──────────────────────────────────────────────────────────────
[0-15] 16 HLHeader CommandID = 10002, PayloadLen = 22
[16-31] 16 Response XXTEA-decrypted challenge
[32-35] 4 UIDPrefix First 4 bytes of UID
[32-35] 4 SessionID Random 4-byte session identifier
[36] 1 VideoFlag 1 = enable video stream
[37] 1 AudioFlag 1 = enable audio stream
```
@@ -620,6 +604,22 @@ Offset Size Field Description
| 0xF0 (240) | Maximum |
| 0x3C (60) | SD quality |
### K10052 - Set Resolution Doorbell (22 bytes)
Used by doorbell models (WYZEDB3, WVOD1, HL_WCO2, WYZEC1) instead of K10056:
```
Offset Size Field Description
──────────────────────────────────────────────────────────────
[0-15] 16 HLHeader CommandID = 10052, PayloadLen = 6
[16-17] 2 Bitrate KB/s value (LE)
[18] 1 FrameSize Resolution + 1 (see table above)
[19] 1 FPS Frames per second, 0 = auto
[20-21] 2 Reserved Zero-filled
```
**Note:** K10052 has a different field order than K10056 (bitrate before frameSize).
---
## 9. AV Frame Structure
@@ -1155,12 +1155,14 @@ authkey = b64.replace('+', 'Z').replace('/', '9').replace('=', 'A')
| Command | ID | Description |
|---------|-----|-------------|
| KCmdAuth | 10000 | Auth request |
| KCmdAuth | 10000 | Auth request (with JSON) |
| KCmdChallenge | 10001 | Challenge from camera |
| KCmdChallengeResp | 10002 | Challenge response |
| KCmdAuthResult | 10003 | Auth result (JSON) |
| KCmdControlChannel | 10010 | Start/stop media |
| KCmdControlChannelResp | 10011 | Control response |
| KCmdSetResolutionDB | 10052 | Set resolution (doorbell) |
| KCmdSetResolutionDBResp | 10053 | Resolution response (doorbell) |
| KCmdSetResolution | 10056 | Set resolution/bitrate |
| KCmdSetResolutionResp | 10057 | Resolution response |
+16 -13
View File
@@ -222,17 +222,19 @@ func (c *Conn) AVServStart() error {
func (c *Conn) AVServStop() error {
c.mu.Lock()
defer c.mu.Unlock()
serverConn := c.serverConn
c.serverConn = nil
// Reset audio TX state
c.audioSeq = 0
c.audioFrameNo = 0
c.mu.Unlock()
if c.serverConn != nil {
err := c.serverConn.Close()
c.serverConn = nil
return err
if serverConn == nil {
return nil
}
go serverConn.Close()
return nil
}
@@ -339,8 +341,13 @@ func (c *Conn) WriteAndWaitIOCtrl(cmd uint16, payload []byte, expectCmd uint16,
frame := c.buildIOCtrlFrame(payload)
var t *time.Timer
t = time.AfterFunc(1, func() {
if _, err := c.clientConn.Write(frame); err == nil && t != nil {
t.Reset(time.Second)
c.mu.RLock()
conn := c.clientConn
c.mu.RUnlock()
if conn != nil {
if _, err := conn.Write(frame); err == nil && t != nil {
t.Reset(time.Second)
}
}
})
defer t.Stop()
@@ -399,10 +406,6 @@ func (c *Conn) Close() error {
c.clientConn.Close()
c.clientConn = nil
}
if c.serverConn != nil {
c.serverConn.Close()
c.serverConn = nil
}
if c.frames != nil {
c.frames.Close()
}
@@ -705,7 +708,7 @@ func (c *Conn) handleSpeakerAVLogin() error {
}
buf := make([]byte, 1024)
c.serverConn.SetReadDeadline(time.Now().Add(5 * time.Second))
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)
+50 -4
View File
@@ -2,6 +2,7 @@ package tutk
import (
"net"
"sync"
"time"
"github.com/pion/dtls/v3"
@@ -42,6 +43,9 @@ func buildDtlsConfig(psk []byte, isServer bool) *dtls.Config {
type ChannelAdapter struct {
conn *Conn
channel uint8
mu sync.Mutex
readDeadline time.Time
}
func (a *ChannelAdapter) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
@@ -52,6 +56,29 @@ func (a *ChannelAdapter) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
buf = a.conn.serverBuf
}
a.mu.Lock()
deadline := a.readDeadline
a.mu.Unlock()
if !deadline.IsZero() {
timeout := time.Until(deadline)
if timeout <= 0 {
return 0, nil, &timeoutError{}
}
timer := time.NewTimer(timeout)
defer timer.Stop()
select {
case data := <-buf:
return copy(p, data), a.conn.addr, nil
case <-timer.C:
return 0, nil, &timeoutError{}
case <-a.conn.ctx.Done():
return 0, nil, net.ErrClosed
}
}
select {
case data := <-buf:
return copy(p, data), a.conn.addr, nil
@@ -67,8 +94,27 @@ func (a *ChannelAdapter) WriteTo(p []byte, _ net.Addr) (int, error) {
return len(p), nil
}
func (a *ChannelAdapter) Close() error { return nil }
func (a *ChannelAdapter) LocalAddr() net.Addr { return &net.UDPAddr{} }
func (a *ChannelAdapter) SetDeadline(time.Time) error { return nil }
func (a *ChannelAdapter) SetReadDeadline(time.Time) error { return nil }
func (a *ChannelAdapter) Close() error { return nil }
func (a *ChannelAdapter) LocalAddr() net.Addr { return &net.UDPAddr{} }
func (a *ChannelAdapter) SetDeadline(t time.Time) error {
a.mu.Lock()
a.readDeadline = t
a.mu.Unlock()
return nil
}
func (a *ChannelAdapter) SetReadDeadline(t time.Time) error {
a.mu.Lock()
a.readDeadline = t
a.mu.Unlock()
return nil
}
func (a *ChannelAdapter) SetWriteDeadline(time.Time) error { return nil }
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }
+22 -1
View File
@@ -4,6 +4,7 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
"sync"
"github.com/AlexxIT/go2rtc/pkg/aac"
)
@@ -282,6 +283,8 @@ type FrameHandler struct {
audioTS tsTracker
output chan *Packet
verbose bool
closed bool
closeMu sync.Mutex
}
func NewFrameHandler(verbose bool) *FrameHandler {
@@ -297,6 +300,13 @@ func (h *FrameHandler) Recv() <-chan *Packet {
}
func (h *FrameHandler) Close() {
h.closeMu.Lock()
defer h.closeMu.Unlock()
if h.closed {
return
}
h.closed = true
close(h.output)
}
@@ -540,6 +550,13 @@ func (h *FrameHandler) handleAudio(payload []byte, fi *FrameInfo) {
}
func (h *FrameHandler) queue(pkt *Packet) {
h.closeMu.Lock()
defer h.closeMu.Unlock()
if h.closed {
return
}
select {
case h.output <- pkt:
default:
@@ -548,7 +565,11 @@ func (h *FrameHandler) queue(pkt *Packet) {
case <-h.output:
default:
}
h.output <- pkt
select {
case h.output <- pkt:
default:
// Queue still full, drop this packet
}
}
}