Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/homekit/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func discovery() ([]*api.Source, error) {
log.Trace().Msgf("[homekit] mdns=%s", entry)

category := entry.Info[hap.TXTCategory]
if entry.Complete() && (category == hap.CategoryCamera || category == hap.CategoryDoorbell) {
if entry.Complete() && (category == hap.CategoryBridge || category == hap.CategoryCamera || category == hap.CategoryDoorbell) {
source := &api.Source{
Name: entry.Name,
Info: entry.Info[hap.TXTModel],
Expand Down
3 changes: 3 additions & 0 deletions pkg/hap/camera/s113_speaker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package camera

const TypeSpeaker = "113"
64 changes: 19 additions & 45 deletions pkg/hap/tlv8/tlv8.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"reflect"
Expand Down Expand Up @@ -186,57 +185,32 @@ func Unmarshal(data []byte, v any) error {
}

func unmarshalStruct(b []byte, value reflect.Value) error {
var waitSlice bool

for len(b) >= 2 {
t := b[0]
l := int(b[1])

// array item divider
if t == 0 && l == 0 {
b = b[2:]
waitSlice = true
continue
}

var v []byte

for {
if len(b) < 2+l {
return errors.New("tlv8: wrong size: " + value.Type().Name())
}

v = append(v, b[2:2+l]...)
b = b[2+l:]

// if size == 255 and same tag - continue read big payload
if l < 255 || len(b) < 2 || b[0] != t {
break
}

l = int(b[1])
}

tag := strconv.Itoa(int(t))

valueField, ok := getStructField(value, tag)
if !ok {
return fmt.Errorf("tlv8: can't find T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
}
if len(b) < 2 {
return nil // End of payload, nothing left to process
}

if waitSlice {
if valueField.Kind() != reflect.Slice {
return fmt.Errorf("tlv8: should be slice T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
}
waitSlice = false
}
t := b[0] // Type
l := int(b[1]) // Length
v := b[2 : 2+l] // Value (can be empty if l == 0)
remainder := b[2+l:] // Move cursor to the next TLV item
tag := strconv.Itoa(int(t))
valueField, ok := getStructField(value, tag)

// Accumulate fragments if the value length is 255
for l == 255 && len(remainder) >= 2 && remainder[0] == t {
l = int(remainder[1])
v = append(v, remainder[2:2+l]...)
remainder = remainder[2+l:]
}

if ok {
if err := unmarshalValue(v, valueField); err != nil {
return err
}
}

return nil
// Recursively process the remaining payload
return unmarshalStruct(remainder, value)
}

func unmarshalValue(v []byte, value reflect.Value) error {
Expand Down
4 changes: 2 additions & 2 deletions pkg/homekit/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ func videoToMedia(codecs []camera.VideoCodec) *core.Media {
var audioCodecs = [...]string{core.CodecPCMU, core.CodecPCMA, core.CodecELD, core.CodecOpus}
var audioSampleRates = [...]uint32{8000, 16000, 24000}

func audioToMedia(codecs []camera.AudioCodec) *core.Media {
func audioToMedia(codecs []camera.AudioCodec, direction string) *core.Media {
media := &core.Media{
Kind: core.KindAudio, Direction: core.DirectionRecvonly,
Kind: core.KindAudio, Direction: direction,
}

for _, codec := range codecs {
Expand Down
73 changes: 41 additions & 32 deletions pkg/homekit/producer.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (c *Client) GetMedias() []*core.Media {

c.Medias = []*core.Media{
videoToMedia(c.videoConfig.Codecs),
audioToMedia(c.audioConfig.Codecs),
audioToMedia(c.audioConfig.Codecs, core.DirectionRecvonly),
{
Kind: core.KindVideo,
Direction: core.DirectionRecvonly,
Expand All @@ -116,9 +116,49 @@ func (c *Client) GetMedias() []*core.Media {
},
}

if acc.GetService(camera.TypeSpeaker) != nil {
c.Medias = append(c.Medias,
audioToMedia(c.audioConfig.Codecs, core.DirectionSendonly),
&core.Media{
Kind: core.KindAudio,
Direction: core.DirectionSendonly,
Codecs: []*core.Codec{
{
Name: core.CodecOpus,
ClockRate: 48000,
Channels: 2,
},
},
},
)
}

return c.Medias
}

func (c *Client) AddTrack(media *core.Media, codec *core.Codec, track *core.Receiver) error {
switch codec.Name {
case core.CodecOpus:
sender := core.NewSender(media, track.Codec)

sender.Handler = func(packet *rtp.Packet) {
if c.audioSession != nil {
packet.PayloadType = 110
packet.SSRC = c.audioSession.Local.SSRC

if n, err := c.audioSession.WriteRTP(packet); err == nil {
c.Send += n
}
}
}

sender.HandleRTP(track)
c.Senders = append(c.Senders, sender)
}

return nil
}

func (c *Client) Start() error {
if c.Receivers == nil {
return errors.New("producer without tracks")
Expand Down Expand Up @@ -169,10 +209,6 @@ func (c *Client) Start() error {
}
}

if c.audioSession.OnReadRTP != nil {
c.audioSession.OnReadRTP = timekeeper(c.audioSession.OnReadRTP)
}

<-deadline.C

return nil
Expand Down Expand Up @@ -226,30 +262,3 @@ func (c *Client) srtpEndpoint() *srtp.Endpoint {
SSRC: rand.Uint32(),
}
}

func timekeeper(handler core.HandlerFunc) core.HandlerFunc {
const sampleRate = 16000
const sampleSize = 480

var send time.Duration
var firstTime time.Time

return func(packet *rtp.Packet) {
now := time.Now()

if send != 0 {
elapsed := now.Sub(firstTime) * sampleRate / time.Second
if send+sampleSize > elapsed {
return // drop overflow frame
}
} else {
firstTime = now
}

send += sampleSize

packet.Timestamp = uint32(send)

handler(packet)
}
}
4 changes: 2 additions & 2 deletions pkg/srtp/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ func (s *Session) WriteRTP(packet *rtp.Packet) (int, error) {
Header: rtp.Header{
Version: 2,
Marker: packet.Marker,
PayloadType: s.PayloadType,
PayloadType: packet.PayloadType,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line and a few lines down is what was preventing me from changing the payload type and ssrc on the rtp packets

SequenceNumber: packet.SequenceNumber,
Timestamp: packet.Timestamp,
SSRC: s.Local.SSRC,
SSRC: packet.SSRC,
},
Payload: packet.Payload,
}
Expand Down