Add support two-way audio for Dafang camera
This commit is contained in:
@@ -23,7 +23,8 @@ func (p *Producer) AddTrack(media *core.Media, _ *core.Codec, track *core.Receiv
|
|||||||
case core.CodecPCMA:
|
case core.CodecPCMA:
|
||||||
var buf []byte
|
var buf []byte
|
||||||
|
|
||||||
if p.model == "isa.camera.hlc6" {
|
switch p.model {
|
||||||
|
case "isa.camera.hlc6", "isa.camera.df3":
|
||||||
dst := &core.Codec{Name: core.CodecPCML, ClockRate: 8000}
|
dst := &core.Codec{Name: core.CodecPCML, ClockRate: 8000}
|
||||||
transcode := pcm.Transcode(dst, track.Codec)
|
transcode := pcm.Transcode(dst, track.Codec)
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ func (p *Producer) AddTrack(media *core.Media, _ *core.Codec, track *core.Receiv
|
|||||||
buf = buf[size:]
|
buf = buf[size:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
sender.Handler = func(pkt *rtp.Packet) {
|
sender.Handler = func(pkt *rtp.Packet) {
|
||||||
buf = append(buf, pkt.Payload...)
|
buf = append(buf, pkt.Payload...)
|
||||||
const size = 8000 * 0.040 // 8bit 40 ms
|
const size = 8000 * 0.040 // 8bit 40 ms
|
||||||
|
|||||||
@@ -140,14 +140,12 @@ func probe(client *miss.Client, channel, quality, audio uint8) ([]*core.Media, e
|
|||||||
Codecs: []*core.Codec{acodec},
|
Codecs: []*core.Codec{acodec},
|
||||||
})
|
})
|
||||||
|
|
||||||
if client.Protocol() == "cs2+udp" {
|
|
||||||
medias = append(medias, &core.Media{
|
medias = append(medias, &core.Media{
|
||||||
Kind: core.KindAudio,
|
Kind: core.KindAudio,
|
||||||
Direction: core.DirectionSendonly,
|
Direction: core.DirectionSendonly,
|
||||||
Codecs: []*core.Codec{acodec.Clone()},
|
Codecs: []*core.Codec{acodec.Clone()},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return medias, nil
|
return medias, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ type Conn struct {
|
|||||||
seqSendCmd1 uint16
|
seqSendCmd1 uint16
|
||||||
seqSendCmd2 uint16
|
seqSendCmd2 uint16
|
||||||
seqSendCnt uint16
|
seqSendCnt uint16
|
||||||
|
seqSendAud uint16
|
||||||
|
|
||||||
seqRecvPkt0 uint16
|
seqRecvPkt0 uint16
|
||||||
seqRecvPkt1 uint16
|
seqRecvPkt1 uint16
|
||||||
@@ -249,7 +250,7 @@ func (c *Conn) ReadPacket() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) WritePacket(data []byte) error {
|
func (c *Conn) WritePacket(data []byte) error {
|
||||||
panic("not implemented")
|
return c.WriteCh1(c.oldMsgAud(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
func genSID() []byte {
|
func genSID() []byte {
|
||||||
|
|||||||
+65
-10
@@ -75,7 +75,10 @@ const (
|
|||||||
msgMediaFrame
|
msgMediaFrame
|
||||||
msgMediaLost
|
msgMediaLost
|
||||||
msgCh5
|
msgCh5
|
||||||
|
msgUnknown0007
|
||||||
|
msgUnknown0008
|
||||||
msgUnknown0010
|
msgUnknown0010
|
||||||
|
msgUnknown0013
|
||||||
msgUnknown0a08
|
msgUnknown0a08
|
||||||
msgDafang0012
|
msgDafang0012
|
||||||
msgDafang0071
|
msgDafang0071
|
||||||
@@ -110,16 +113,29 @@ func (c *Conn) handleMsg(msg []byte) int8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) handleCh1(cmd []byte) int8 {
|
func (c *Conn) handleCh1(cmd []byte) int8 {
|
||||||
|
// Channel 1 used for two-way audio. It's important:
|
||||||
|
// - answer on 0000 command with exact config response (can't set simple proto)
|
||||||
|
// - send 0012 command at start
|
||||||
|
// - respond on every 0008 command for smooth playback
|
||||||
switch cid := string(cmd[:2]); cid {
|
switch cid := string(cmd[:2]); cid {
|
||||||
case "\x00\x00":
|
case "\x00\x00": // client start
|
||||||
_ = c.WriteCh1(c.msgAck0000(cmd))
|
_ = c.WriteCh1(c.msgAck0000(cmd))
|
||||||
|
_ = c.WriteCh1(c.msg0012())
|
||||||
return msgClientStart00
|
return msgClientStart00
|
||||||
case "\x00\x20":
|
case "\x00\x07": // time sync without data
|
||||||
|
_ = c.WriteCh1(c.msgAck0007(cmd))
|
||||||
|
return msgUnknown0007
|
||||||
|
case "\x00\x08": // time sync with data
|
||||||
|
_ = c.WriteCh1(c.msgAck0008(cmd))
|
||||||
|
return msgUnknown0008
|
||||||
|
case "\x00\x13": // ack for 0012
|
||||||
|
return msgUnknown0013
|
||||||
|
case "\x00\x20": // client start2
|
||||||
//_ = c.WriteCh1(c.msgAck0020(cmd))
|
//_ = c.WriteCh1(c.msgAck0020(cmd))
|
||||||
return msgClientStart20
|
return msgClientStart20
|
||||||
case "\x09\x00": // skip
|
case "\x09\x00": // counters sync
|
||||||
return msgCounters
|
return msgCounters
|
||||||
case "\x0a\x08":
|
case "\x0a\x08": // unknown
|
||||||
_ = c.WriteCh1(c.msgAck0A08(cmd))
|
_ = c.WriteCh1(c.msgAck0A08(cmd))
|
||||||
return msgUnknown0a08
|
return msgUnknown0a08
|
||||||
}
|
}
|
||||||
@@ -143,8 +159,9 @@ const msgHhrSize = 28
|
|||||||
const cmdHdrSize = 24
|
const cmdHdrSize = 24
|
||||||
|
|
||||||
func (c *Conn) msgAck0000(msg28 []byte) []byte {
|
func (c *Conn) msgAck0000(msg28 []byte) []byte {
|
||||||
const cmdDataSize = 36
|
// <- 000008000000000000000000000000001a0200004f47c714 ... 00000000000000000100000004000000fb071f00000000000000000000000300
|
||||||
|
// -> 00140b00000000000000000000000000200000004f47c714 00000000000000000100000004000000fb071f00000000000000000000000300
|
||||||
|
const cmdDataSize = 32
|
||||||
msg := c.msg(msgHhrSize + cmdHdrSize + cmdDataSize)
|
msg := c.msg(msgHhrSize + cmdHdrSize + cmdDataSize)
|
||||||
|
|
||||||
cmd := msg[msgHhrSize:]
|
cmd := msg[msgHhrSize:]
|
||||||
@@ -152,9 +169,9 @@ func (c *Conn) msgAck0000(msg28 []byte) []byte {
|
|||||||
cmd[16] = cmdDataSize
|
cmd[16] = cmdDataSize
|
||||||
copy(cmd[20:], msg28[20:24]) // request id (random)
|
copy(cmd[20:], msg28[20:24]) // request id (random)
|
||||||
|
|
||||||
// It's better not to answer anything, so camera won't send anything to this channel.
|
// Important to answer with same data.
|
||||||
//data := cmd[cmdHdrSize:]
|
data := cmd[cmdHdrSize:]
|
||||||
//copy(data, msg28[len(msg28)-32:])
|
copy(data, msg28[len(msg28)-32:])
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,8 +196,46 @@ func (c *Conn) msgAck0000(msg28 []byte) []byte {
|
|||||||
// return msg
|
// return msg
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
func (c *Conn) msg0012() []byte {
|
||||||
|
// -> 00120b000000000000000000000000000c00000000000000020000000100000001000000
|
||||||
|
const dataSize = 12
|
||||||
|
msg := c.msg(msgHhrSize + cmdHdrSize + dataSize)
|
||||||
|
cmd := msg[msgHhrSize:]
|
||||||
|
|
||||||
|
copy(cmd, "\x00\x12\x0b\x00")
|
||||||
|
cmd[16] = dataSize
|
||||||
|
data := cmd[cmdHdrSize:]
|
||||||
|
|
||||||
|
data[0] = 2
|
||||||
|
data[4] = 1
|
||||||
|
data[9] = 1
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) msgAck0007(msg28 []byte) []byte {
|
||||||
|
// <- 000708000000000000000000000000000c00000001000000000000001c551f7a00000000
|
||||||
|
// -> 010a0b00000000000000000000000000000000000100000000000000
|
||||||
|
msg := c.msg(msgHhrSize + 28)
|
||||||
|
cmd := msg[msgHhrSize:]
|
||||||
|
copy(cmd, "\x01\x0a\x0b\x00")
|
||||||
|
cmd[20] = 1
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) msgAck0008(msg28 []byte) []byte {
|
||||||
|
// <- 000808000000000000000000000000000000f9f0010000000200000050f31f7a
|
||||||
|
// -> 01090b0000000000000000000000000000000000010000000200000050f31f7a
|
||||||
|
msg := c.msg(msgHhrSize + 28)
|
||||||
|
cmd := msg[msgHhrSize:]
|
||||||
|
copy(cmd, "\x01\x09\x0b\x00")
|
||||||
|
copy(cmd[20:], msg28[20:])
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) msgAck0A08(msg28 []byte) []byte {
|
func (c *Conn) msgAck0A08(msg28 []byte) []byte {
|
||||||
msg := c.msg(48)
|
// <- 0a080b005b0000000b51590002000000
|
||||||
|
// -> 0b000b00000001000b5103000300000000000000
|
||||||
|
msg := c.msg(msgHhrSize + 20)
|
||||||
cmd := msg[msgHhrSize:]
|
cmd := msg[msgHhrSize:]
|
||||||
copy(cmd, "\x0b\x00\x0b\x00")
|
copy(cmd, "\x0b\x00\x0b\x00")
|
||||||
copy(cmd[8:], msg28[8:10])
|
copy(cmd[8:], msg28[8:10])
|
||||||
|
|||||||
@@ -186,6 +186,6 @@ func (c *Conn) msgAckCounters() []byte {
|
|||||||
|
|
||||||
binary.LittleEndian.PutUint16(cmd[18:], c.seqSendCnt)
|
binary.LittleEndian.PutUint16(cmd[18:], c.seqSendCnt)
|
||||||
c.seqSendCnt++
|
c.seqSendCnt++
|
||||||
binary.LittleEndian.PutUint16(cmd[20:], uint16(time.Now().UnixNano()))
|
binary.LittleEndian.PutUint16(cmd[20:], uint16(time.Now().UnixMilli()))
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ package tutk
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Conn) oldMsgCtrl(ctrlType uint16, ctrlData []byte) []byte {
|
func (c *Conn) oldMsgCtrl(ctrlType uint16, ctrlData []byte) []byte {
|
||||||
size := msgHhrSize + cmdHdrSize + 4 + uint16(len(ctrlData))
|
dataSize := 4 + uint16(len(ctrlData))
|
||||||
msg := c.msg(size)
|
msg := c.msg(msgHhrSize + cmdHdrSize + dataSize)
|
||||||
|
|
||||||
cmd := msg[msgHhrSize:]
|
cmd := msg[msgHhrSize:]
|
||||||
copy(cmd, "\x00\x70\x0b\x00")
|
copy(cmd, "\x00\x70\x0b\x00")
|
||||||
@@ -15,7 +16,7 @@ func (c *Conn) oldMsgCtrl(ctrlType uint16, ctrlData []byte) []byte {
|
|||||||
binary.LittleEndian.PutUint16(cmd[4:], c.seqSendCmd1)
|
binary.LittleEndian.PutUint16(cmd[4:], c.seqSendCmd1)
|
||||||
c.seqSendCmd1++
|
c.seqSendCmd1++
|
||||||
|
|
||||||
binary.LittleEndian.PutUint16(cmd[16:], size-52)
|
binary.LittleEndian.PutUint16(cmd[16:], dataSize)
|
||||||
//binary.LittleEndian.PutUint32(cmd[20:], uint32(time.Now().UnixMilli()))
|
//binary.LittleEndian.PutUint32(cmd[20:], uint32(time.Now().UnixMilli()))
|
||||||
|
|
||||||
data := cmd[cmdHdrSize:]
|
data := cmd[cmdHdrSize:]
|
||||||
@@ -24,6 +25,43 @@ func (c *Conn) oldMsgCtrl(ctrlType uint16, ctrlData []byte) []byte {
|
|||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pktHdrSize = 32
|
||||||
|
|
||||||
|
func (c *Conn) oldMsgAud(pkt []byte) []byte {
|
||||||
|
// -> 01030b001d0000008802000000002800b0020bf501000000 ... 4f4455412000000088020000030400001d000000000000000bf51f7a9b0100000000000000000000
|
||||||
|
hdr := pkt[:pktHdrSize]
|
||||||
|
payload := pkt[pktHdrSize:]
|
||||||
|
|
||||||
|
n := uint16(len(payload))
|
||||||
|
dataSize := n + 8 + 32
|
||||||
|
msg := c.msg(msgHhrSize + cmdHdrSize + dataSize)
|
||||||
|
|
||||||
|
// 0 01030b00 command + version
|
||||||
|
// 4 1d000000 seq
|
||||||
|
// 8 8802 media size (648)
|
||||||
|
// 10 00000000
|
||||||
|
// 14 2800 tail (pkt header) size?
|
||||||
|
// 16 b002 size (648 + 8 + 32)
|
||||||
|
// 18 0bf5 random msg id (unixms)
|
||||||
|
// 20 01000000 fixed
|
||||||
|
cmd := msg[msgHhrSize:]
|
||||||
|
copy(cmd, "\x01\x03\x0b\x00")
|
||||||
|
binary.LittleEndian.PutUint16(cmd[4:], c.seqSendAud)
|
||||||
|
c.seqSendAud++
|
||||||
|
binary.LittleEndian.PutUint16(cmd[8:], n)
|
||||||
|
cmd[14] = 0x28 // important!
|
||||||
|
binary.LittleEndian.PutUint16(cmd[16:], dataSize)
|
||||||
|
binary.LittleEndian.PutUint16(cmd[18:], uint16(time.Now().UnixMilli()))
|
||||||
|
cmd[20] = 1
|
||||||
|
|
||||||
|
data := cmd[cmdHdrSize:]
|
||||||
|
copy(data, payload)
|
||||||
|
copy(data[n:], "ODUA\x20\x00\x00\x00")
|
||||||
|
copy(data[n+8:], hdr)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) oldHandlerCh0() func([]byte) int8 {
|
func (c *Conn) oldHandlerCh0() func([]byte) int8 {
|
||||||
var waitSeq uint16
|
var waitSeq uint16
|
||||||
var waitSize uint32
|
var waitSize uint32
|
||||||
@@ -34,13 +72,15 @@ func (c *Conn) oldHandlerCh0() func([]byte) int8 {
|
|||||||
// 4 00000000 fixed
|
// 4 00000000 fixed
|
||||||
// 8 ac880100 total size
|
// 8 ac880100 total size
|
||||||
// 12 6200 chunk seq
|
// 12 6200 chunk seq
|
||||||
// 14 2000 ???
|
// 14 2000 tail (pkt header) size?
|
||||||
// 16 cc00 size
|
// 16 cc00 size
|
||||||
// 18 0000
|
// 18 0000
|
||||||
// 20 01000000 fixed
|
// 20 01000000 fixed
|
||||||
|
|
||||||
switch cmd[0] {
|
switch cmd[0] {
|
||||||
case 0x01:
|
case 0x01:
|
||||||
|
var packetData []byte
|
||||||
|
|
||||||
switch cmd[1] {
|
switch cmd[1] {
|
||||||
case 0x03:
|
case 0x03:
|
||||||
seq := binary.LittleEndian.Uint16(cmd[12:])
|
seq := binary.LittleEndian.Uint16(cmd[12:])
|
||||||
@@ -62,33 +102,33 @@ func (c *Conn) oldHandlerCh0() func([]byte) int8 {
|
|||||||
return msgMediaLost
|
return msgMediaLost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
waitSeq = 0
|
||||||
|
|
||||||
// create a buffer for the header and collected data
|
// create a buffer for the header and collected data
|
||||||
packetData := make([]byte, waitSize)
|
packetData = make([]byte, waitSize)
|
||||||
// there's a header at the end - let's move it to the beginning
|
// there's a header at the end - let's move it to the beginning
|
||||||
copy(packetData, waitData[waitSize-32:])
|
copy(packetData, waitData[waitSize-32:])
|
||||||
copy(packetData[32:], waitData)
|
copy(packetData[32:], waitData)
|
||||||
|
|
||||||
select {
|
|
||||||
case c.rawPkt <- packetData:
|
|
||||||
default:
|
|
||||||
c.err = fmt.Errorf("%s: media queue is full", "tutk")
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
waitSeq = 0
|
|
||||||
return msgMediaFrame
|
|
||||||
|
|
||||||
case 0x04:
|
case 0x04:
|
||||||
|
// This is audio from miss audio start command. MiHome not using miss commands.
|
||||||
waitSize2 := binary.LittleEndian.Uint32(cmd[8:])
|
waitSize2 := binary.LittleEndian.Uint32(cmd[8:])
|
||||||
waitData2 := cmd[24:]
|
waitData2 := cmd[24:]
|
||||||
|
|
||||||
if uint32(len(waitData2)) != waitSize2 {
|
if uint32(len(waitData2)) != waitSize2 {
|
||||||
return -1 // shouldn't happened for audio
|
return -1 // shouldn't happen for audio
|
||||||
}
|
}
|
||||||
|
|
||||||
packetData := make([]byte, waitSize2)
|
packetData = make([]byte, waitSize2)
|
||||||
copy(packetData, waitData2)
|
copy(packetData, waitData2)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix Dafang bug (timestamp in seconds)
|
||||||
|
binary.LittleEndian.PutUint64(packetData[16:], uint64(time.Now().UnixMilli()))
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case c.rawPkt <- packetData:
|
case c.rawPkt <- packetData:
|
||||||
default:
|
default:
|
||||||
@@ -96,7 +136,6 @@ func (c *Conn) oldHandlerCh0() func([]byte) int8 {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return msgMediaFrame
|
return msgMediaFrame
|
||||||
}
|
|
||||||
|
|
||||||
case 0x00:
|
case 0x00:
|
||||||
switch cmd[1] {
|
switch cmd[1] {
|
||||||
@@ -123,9 +162,9 @@ func (c *Conn) oldHandlerCh0() func([]byte) int8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) msgAck0070(msg28 []byte) []byte {
|
func (c *Conn) msgAck0070(msg28 []byte) []byte {
|
||||||
// <- [104] 0402120a 58000300 08041200 e6e8 0000 0c000000e6e839da66b0dc14 00700800010000000000000000000000 3400 00007625a02f ...
|
// <- 00700800010000000000000000000000340000007625a02f ...
|
||||||
// -> [ 52] 0402190a 24000400 07042100 e6e8 0000 0c000000e6e839da66b0dc14 00710800010000000000000000000000 0000 00007625a02f
|
// -> 00710800010000000000000000000000000000007625a02f
|
||||||
msg := c.msg(52)
|
msg := c.msg(msgHhrSize + cmdHdrSize)
|
||||||
|
|
||||||
cmd := msg[msgHhrSize:]
|
cmd := msg[msgHhrSize:]
|
||||||
copy(cmd, "\x00\x71\x0b\x00")
|
copy(cmd, "\x00\x71\x0b\x00")
|
||||||
@@ -137,14 +176,14 @@ func (c *Conn) msgAck0070(msg28 []byte) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) msgAck0012(msg28 []byte) []byte {
|
func (c *Conn) msgAck0012(msg28 []byte) []byte {
|
||||||
// <- [64] 0402120a 30000000 08041200 e6e800000c000000e6e839da66b0dc14 001208000000000000000000000000000c00000000000000 020000000100000001000000
|
// <- 001208000000000000000000000000000c00000000000000 020000000100000001000000
|
||||||
// -> [72] 0402190a 38000300 07042100 e6e800000c000000e6e839da66b0dc14 00130b000000000000000000000000001400000000000000 0200000001000000010000000000000000000000
|
// -> 00130b000000000000000000000000001400000000000000 0200000001000000010000000000000000000000
|
||||||
const size = 72
|
const dataSize = 20
|
||||||
msg := c.msg(size)
|
msg := c.msg(msgHhrSize + cmdHdrSize + dataSize)
|
||||||
|
|
||||||
cmd := msg[msgHhrSize:]
|
cmd := msg[msgHhrSize:]
|
||||||
copy(cmd, "\x00\x13\x0b\x00")
|
copy(cmd, "\x00\x13\x0b\x00")
|
||||||
cmd[16] = size - 52 // data size
|
cmd[16] = dataSize
|
||||||
|
|
||||||
data := cmd[cmdHdrSize:]
|
data := cmd[cmdHdrSize:]
|
||||||
copy(data, msg28[cmdHdrSize:])
|
copy(data, msg28[cmdHdrSize:])
|
||||||
|
|||||||
Reference in New Issue
Block a user