fix(homekit): fix HKSV recording by correcting HDS protocol and adding GOP buffering
The HKSV recording was failing because: 1. The dataSend.data message structure was wrong - `packets` was a flat integer instead of an array of objects with `data` and `metadata` fields matching the HAP-NodeJS specification 2. Each video/audio frame was sent as a separate mediaFragment, but Home Hub expects GOP-based fragments (~2-4 seconds of accumulated data) 3. Large fragments were not chunked (max 256 KiB per chunk) Changes: - Fix HDS dataSend.data message structure to use proper packets array with nested data/metadata (dataType, dataSequenceNumber, dataChunkSequenceNumber, isLastDataChunk, dataTotalSize) - Add 256 KiB chunking for large media fragments - Buffer moof+mdat pairs in hksvConsumer and flush on keyframe boundaries (GOP-based fragmentation) - Pre-start consumer at pair-verify for instant init segment delivery - Add write-response support to HAP PUT handler for ch131 DataStream setup - Fix HAP service linking to match HAP-NodeJS reference - Add default SelectedCameraRecordingConfiguration (ch209) value - Start continuous motion generator at pair-verify with dedup protection
This commit is contained in:
+48
-16
@@ -163,27 +163,59 @@ func (s *Session) WriteRequest(protocol, topic string, body map[string]any) (int
|
||||
return id, s.WriteMessage(header, body)
|
||||
}
|
||||
|
||||
// maxChunkSize is the maximum data chunk size for HDS media transfer (256 KiB)
|
||||
const maxChunkSize = 0x40000
|
||||
|
||||
// SendMediaInit sends the fMP4 initialization segment (ftyp+moov)
|
||||
func (s *Session) SendMediaInit(streamID int, initData []byte) error {
|
||||
return s.WriteEvent(ProtoDataSend, TopicData, map[string]any{
|
||||
"streamId": streamID,
|
||||
"packets": 1,
|
||||
"type": "mediaInitialization",
|
||||
"data": initData,
|
||||
})
|
||||
return s.sendMediaData(streamID, "mediaInitialization", initData, 1)
|
||||
}
|
||||
|
||||
// SendMediaFragment sends an fMP4 fragment (moof+mdat)
|
||||
// SendMediaFragment sends an fMP4 fragment (moof+mdat), splitting into chunks if needed
|
||||
func (s *Session) SendMediaFragment(streamID int, fragment []byte, sequence int) error {
|
||||
return s.WriteEvent(ProtoDataSend, TopicData, map[string]any{
|
||||
"streamId": streamID,
|
||||
"packets": 1,
|
||||
"type": "mediaFragment",
|
||||
"data": fragment,
|
||||
"dataSequenceNumber": sequence,
|
||||
"isLastDataChunk": true,
|
||||
"dataChunkSequenceNumber": 0,
|
||||
})
|
||||
return s.sendMediaData(streamID, "mediaFragment", fragment, sequence)
|
||||
}
|
||||
|
||||
// sendMediaData sends media data with proper HAP-NodeJS compatible packet structure.
|
||||
// Large data is split into chunks of maxChunkSize bytes.
|
||||
func (s *Session) sendMediaData(streamID int, dataType string, data []byte, sequence int) error {
|
||||
totalSize := len(data)
|
||||
chunkSeq := 1
|
||||
|
||||
for offset := 0; offset < totalSize; offset += maxChunkSize {
|
||||
end := offset + maxChunkSize
|
||||
if end > totalSize {
|
||||
end = totalSize
|
||||
}
|
||||
chunk := data[offset:end]
|
||||
isLast := end >= totalSize
|
||||
|
||||
metadata := map[string]any{
|
||||
"dataType": dataType,
|
||||
"dataSequenceNumber": sequence,
|
||||
"dataChunkSequenceNumber": chunkSeq,
|
||||
"isLastDataChunk": isLast,
|
||||
}
|
||||
if chunkSeq == 1 {
|
||||
metadata["dataTotalSize"] = totalSize
|
||||
}
|
||||
|
||||
body := map[string]any{
|
||||
"streamId": streamID,
|
||||
"packets": []any{
|
||||
map[string]any{
|
||||
"data": chunk,
|
||||
"metadata": metadata,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := s.WriteEvent(ProtoDataSend, TopicData, body); err != nil {
|
||||
return err
|
||||
}
|
||||
chunkSeq++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run processes incoming HDS messages in a loop
|
||||
|
||||
Reference in New Issue
Block a user