Rename HomeKit structures according to specs

This commit is contained in:
Alex X
2025-11-09 21:24:47 +03:00
parent ff18283d11
commit e4359ac217
21 changed files with 221 additions and 134 deletions
+2 -2
View File
@@ -86,7 +86,7 @@ func (s *server) SetCharacteristic(conn net.Conn, aid uint8, iid uint64, value a
switch char.Type { switch char.Type {
case camera.TypeSetupEndpoints: case camera.TypeSetupEndpoints:
var offer camera.SetupEndpoints var offer camera.SetupEndpointsRequest
if err := tlv8.UnmarshalBase64(value, &offer); err != nil { if err := tlv8.UnmarshalBase64(value, &offer); err != nil {
return return
} }
@@ -95,7 +95,7 @@ func (s *server) SetCharacteristic(conn net.Conn, aid uint8, iid uint64, value a
s.consumer.SetOffer(&offer) s.consumer.SetOffer(&offer)
case camera.TypeSelectedStreamConfiguration: case camera.TypeSelectedStreamConfiguration:
var conf camera.SelectedStreamConfig var conf camera.SelectedStreamConfiguration
if err := tlv8.UnmarshalBase64(value, &conf); err != nil { if err := tlv8.UnmarshalBase64(value, &conf); err != nil {
return return
} }
+3
View File
@@ -0,0 +1,3 @@
## Useful links
- https://github.com/bauer-andreas/secure-video-specification
+13 -13
View File
@@ -49,17 +49,17 @@ func ServiceCameraRTPStreamManagement() *hap.Service {
val120, _ := tlv8.MarshalBase64(StreamingStatus{ val120, _ := tlv8.MarshalBase64(StreamingStatus{
Status: StreamingStatusAvailable, Status: StreamingStatusAvailable,
}) })
val114, _ := tlv8.MarshalBase64(SupportedVideoStreamConfig{ val114, _ := tlv8.MarshalBase64(SupportedVideoStreamConfiguration{
Codecs: []VideoCodec{ Codecs: []VideoCodecConfiguration{
{ {
CodecType: VideoCodecTypeH264, CodecType: VideoCodecTypeH264,
CodecParams: []VideoParams{ CodecParams: []VideoCodecParameters{
{ {
ProfileID: []byte{VideoCodecProfileMain}, ProfileID: []byte{VideoCodecProfileMain},
Level: []byte{VideoCodecLevel31, VideoCodecLevel40}, Level: []byte{VideoCodecLevel31, VideoCodecLevel40},
}, },
}, },
VideoAttrs: []VideoAttrs{ VideoAttrs: []VideoCodecAttributes{
{Width: 1920, Height: 1080, Framerate: 30}, {Width: 1920, Height: 1080, Framerate: 30},
{Width: 1280, Height: 720, Framerate: 30}, // important for iPhones {Width: 1280, Height: 720, Framerate: 30}, // important for iPhones
{Width: 320, Height: 240, Framerate: 15}, // apple watch {Width: 320, Height: 240, Framerate: 15}, // apple watch
@@ -67,23 +67,23 @@ func ServiceCameraRTPStreamManagement() *hap.Service {
}, },
}, },
}) })
val115, _ := tlv8.MarshalBase64(SupportedAudioStreamConfig{ val115, _ := tlv8.MarshalBase64(SupportedAudioStreamConfiguration{
Codecs: []AudioCodec{ Codecs: []AudioCodecConfiguration{
{ {
CodecType: AudioCodecTypeOpus, CodecType: AudioCodecTypeOpus,
CodecParams: []AudioParams{ CodecParams: []AudioCodecParameters{
{ {
Channels: 1, Channels: 1,
Bitrate: AudioCodecBitrateVariable, BitrateMode: AudioCodecBitrateVariable,
SampleRate: []byte{AudioCodecSampleRate16Khz}, SampleRate: []byte{AudioCodecSampleRate16Khz},
}, },
}, },
}, },
}, },
ComfortNoise: 0, ComfortNoiseSupport: 0,
}) })
val116, _ := tlv8.MarshalBase64(SupportedRTPConfig{ val116, _ := tlv8.MarshalBase64(SupportedRTPConfiguration{
CryptoType: []byte{CryptoAES_CM_128_HMAC_SHA1_80}, SRTPCryptoType: []byte{CryptoAES_CM_128_HMAC_SHA1_80},
}) })
service := &hap.Service{ service := &hap.Service{
+39 -39
View File
@@ -63,19 +63,19 @@ func TestAqaraG3(t *testing.T) {
{ {
name: "114", name: "114",
value: "AaoBAQACEQEBAQIBAAAAAgECAwEABAEAAwsBAoAHAgI4BAMBHgAAAwsBAgAFAgLQAgMBHgAAAwsBAoACAgJoAQMBHgAAAwsBAuABAgIOAQMBHgAAAwsBAkABAgK0AAMBHgAAAwsBAgAFAgLAAwMBHgAAAwsBAgAEAgIAAwMBHgAAAwsBAoACAgLgAQMBHgAAAwsBAuABAgJoAQMBHgAAAwsBAkABAgLwAAMBHg==", value: "AaoBAQACEQEBAQIBAAAAAgECAwEABAEAAwsBAoAHAgI4BAMBHgAAAwsBAgAFAgLQAgMBHgAAAwsBAoACAgJoAQMBHgAAAwsBAuABAgIOAQMBHgAAAwsBAkABAgK0AAMBHgAAAwsBAgAFAgLAAwMBHgAAAwsBAgAEAgIAAwMBHgAAAwsBAoACAgLgAQMBHgAAAwsBAuABAgJoAQMBHgAAAwsBAkABAgLwAAMBHg==",
actual: &SupportedVideoStreamConfig{}, actual: &SupportedVideoStreamConfiguration{},
expect: &SupportedVideoStreamConfig{ expect: &SupportedVideoStreamConfiguration{
Codecs: []VideoCodec{ Codecs: []VideoCodecConfiguration{
{ {
CodecType: VideoCodecTypeH264, CodecType: VideoCodecTypeH264,
CodecParams: []VideoParams{ CodecParams: []VideoCodecParameters{
{ {
ProfileID: []byte{VideoCodecProfileMain}, ProfileID: []byte{VideoCodecProfileMain},
Level: []byte{VideoCodecLevel31, VideoCodecLevel40}, Level: []byte{VideoCodecLevel31, VideoCodecLevel40},
CVOEnabled: []byte{0}, CVOEnabled: []byte{0},
}, },
}, },
VideoAttrs: []VideoAttrs{ VideoAttrs: []VideoCodecAttributes{
{Width: 1920, Height: 1080, Framerate: 30}, {Width: 1920, Height: 1080, Framerate: 30},
{Width: 1280, Height: 720, Framerate: 30}, {Width: 1280, Height: 720, Framerate: 30},
{Width: 640, Height: 360, Framerate: 30}, {Width: 640, Height: 360, Framerate: 30},
@@ -94,29 +94,29 @@ func TestAqaraG3(t *testing.T) {
{ {
name: "115", name: "115",
value: "AQ4BAQICCQEBAQIBAAMBAQIBAA==", value: "AQ4BAQICCQEBAQIBAAMBAQIBAA==",
actual: &SupportedAudioStreamConfig{}, actual: &SupportedAudioStreamConfiguration{},
expect: &SupportedAudioStreamConfig{ expect: &SupportedAudioStreamConfiguration{
Codecs: []AudioCodec{ Codecs: []AudioCodecConfiguration{
{ {
CodecType: AudioCodecTypeAACELD, CodecType: AudioCodecTypeAACELD,
CodecParams: []AudioParams{ CodecParams: []AudioCodecParameters{
{ {
Channels: 1, Channels: 1,
Bitrate: AudioCodecBitrateVariable, BitrateMode: AudioCodecBitrateVariable,
SampleRate: []byte{AudioCodecSampleRate16Khz}, SampleRate: []byte{AudioCodecSampleRate16Khz},
}, },
}, },
}, },
}, },
ComfortNoise: 0, ComfortNoiseSupport: 0,
}, },
}, },
{ {
name: "116", name: "116",
value: "AgEAAAACAQEAAAIBAg==", value: "AgEAAAACAQEAAAIBAg==",
actual: &SupportedRTPConfig{}, actual: &SupportedRTPConfiguration{},
expect: &SupportedRTPConfig{ expect: &SupportedRTPConfiguration{
CryptoType: []byte{CryptoAES_CM_128_HMAC_SHA1_80, CryptoAES_CM_256_HMAC_SHA1_80, CryptoNone}, SRTPCryptoType: []byte{CryptoAES_CM_128_HMAC_SHA1_80, CryptoAES_CM_256_HMAC_SHA1_80, CryptoDisabled},
}, },
}, },
} }
@@ -130,18 +130,18 @@ func TestHomebridge(t *testing.T) {
{ {
name: "114", name: "114",
value: "AcUBAQACHQEBAAAAAQEBAAABAQICAQAAAAIBAQAAAgECAwEAAwsBAkABAgK0AAMBHgAAAwsBAkABAgLwAAMBDwAAAwsBAkABAgLwAAMBHgAAAwsBAuABAgIOAQMBHgAAAwsBAuABAgJoAQMBHgAAAwsBAoACAgJoAQMBHgAAAwsBAoACAgLgAQMBHgAAAwsBAgAFAgLQAgMBHgAAAwsBAgAFAgLAAwMBHgAAAwsBAoAHAgI4BAMBHgAAAwsBAkAGAgKwBAMBHg==", value: "AcUBAQACHQEBAAAAAQEBAAABAQICAQAAAAIBAQAAAgECAwEAAwsBAkABAgK0AAMBHgAAAwsBAkABAgLwAAMBDwAAAwsBAkABAgLwAAMBHgAAAwsBAuABAgIOAQMBHgAAAwsBAuABAgJoAQMBHgAAAwsBAoACAgJoAQMBHgAAAwsBAoACAgLgAQMBHgAAAwsBAgAFAgLQAgMBHgAAAwsBAgAFAgLAAwMBHgAAAwsBAoAHAgI4BAMBHgAAAwsBAkAGAgKwBAMBHg==",
actual: &SupportedVideoStreamConfig{}, actual: &SupportedVideoStreamConfiguration{},
expect: &SupportedVideoStreamConfig{ expect: &SupportedVideoStreamConfiguration{
Codecs: []VideoCodec{ Codecs: []VideoCodecConfiguration{
{ {
CodecType: VideoCodecTypeH264, CodecType: VideoCodecTypeH264,
CodecParams: []VideoParams{ CodecParams: []VideoCodecParameters{
{ {
ProfileID: []byte{VideoCodecProfileConstrainedBaseline, VideoCodecProfileMain, VideoCodecProfileHigh}, ProfileID: []byte{VideoCodecProfileConstrainedBaseline, VideoCodecProfileMain, VideoCodecProfileHigh},
Level: []byte{VideoCodecLevel31, VideoCodecLevel32, VideoCodecLevel40}, Level: []byte{VideoCodecLevel31, VideoCodecLevel32, VideoCodecLevel40},
}, },
}, },
VideoAttrs: []VideoAttrs{ VideoAttrs: []VideoCodecAttributes{
{Width: 320, Height: 180, Framerate: 30}, {Width: 320, Height: 180, Framerate: 30},
{Width: 320, Height: 240, Framerate: 15}, {Width: 320, Height: 240, Framerate: 15},
@@ -162,9 +162,9 @@ func TestHomebridge(t *testing.T) {
{ {
name: "116", name: "116",
value: "AgEA", value: "AgEA",
actual: &SupportedRTPConfig{}, actual: &SupportedRTPConfiguration{},
expect: &SupportedRTPConfig{ expect: &SupportedRTPConfiguration{
CryptoType: []byte{CryptoAES_CM_128_HMAC_SHA1_80}, SRTPCryptoType: []byte{CryptoAES_CM_128_HMAC_SHA1_80},
}, },
}, },
} }
@@ -178,18 +178,18 @@ func TestScrypted(t *testing.T) {
{ {
name: "114", name: "114",
value: "AVIBAQACEwEBAQIBAAAAAgEBAAACAQIDAQADCwECAA8CAnAIAwEeAAADCwECgAcCAjgEAwEeAAADCwECAAUCAtACAwEeAAADCwECQAECAvAAAwEP", value: "AVIBAQACEwEBAQIBAAAAAgEBAAACAQIDAQADCwECAA8CAnAIAwEeAAADCwECgAcCAjgEAwEeAAADCwECAAUCAtACAwEeAAADCwECQAECAvAAAwEP",
actual: &SupportedVideoStreamConfig{}, actual: &SupportedVideoStreamConfiguration{},
expect: &SupportedVideoStreamConfig{ expect: &SupportedVideoStreamConfiguration{
Codecs: []VideoCodec{ Codecs: []VideoCodecConfiguration{
{ {
CodecType: VideoCodecTypeH264, CodecType: VideoCodecTypeH264,
CodecParams: []VideoParams{ CodecParams: []VideoCodecParameters{
{ {
ProfileID: []byte{VideoCodecProfileMain}, ProfileID: []byte{VideoCodecProfileMain},
Level: []byte{VideoCodecLevel31, VideoCodecLevel32, VideoCodecLevel40}, Level: []byte{VideoCodecLevel31, VideoCodecLevel32, VideoCodecLevel40},
}, },
}, },
VideoAttrs: []VideoAttrs{ VideoAttrs: []VideoCodecAttributes{
{Width: 3840, Height: 2160, Framerate: 30}, {Width: 3840, Height: 2160, Framerate: 30},
{Width: 1920, Height: 1080, Framerate: 30}, {Width: 1920, Height: 1080, Framerate: 30},
{Width: 1280, Height: 720, Framerate: 30}, {Width: 1280, Height: 720, Framerate: 30},
@@ -202,15 +202,15 @@ func TestScrypted(t *testing.T) {
{ {
name: "115", name: "115",
value: "AScBAQMCIgEBAQIBAAMBAAAAAwEAAAADAQEAAAMBAQAAAwECAAADAQICAQA=", value: "AScBAQMCIgEBAQIBAAMBAAAAAwEAAAADAQEAAAMBAQAAAwECAAADAQICAQA=",
actual: &SupportedAudioStreamConfig{}, actual: &SupportedAudioStreamConfiguration{},
expect: &SupportedAudioStreamConfig{ expect: &SupportedAudioStreamConfiguration{
Codecs: []AudioCodec{ Codecs: []AudioCodecConfiguration{
{ {
CodecType: AudioCodecTypeOpus, CodecType: AudioCodecTypeOpus,
CodecParams: []AudioParams{ CodecParams: []AudioCodecParameters{
{ {
Channels: 1, Channels: 1,
Bitrate: AudioCodecBitrateVariable, BitrateMode: AudioCodecBitrateVariable,
SampleRate: []byte{ SampleRate: []byte{
AudioCodecSampleRate8Khz, AudioCodecSampleRate8Khz, AudioCodecSampleRate8Khz, AudioCodecSampleRate8Khz,
AudioCodecSampleRate16Khz, AudioCodecSampleRate16Khz, AudioCodecSampleRate16Khz, AudioCodecSampleRate16Khz,
@@ -220,15 +220,15 @@ func TestScrypted(t *testing.T) {
}, },
}, },
}, },
ComfortNoise: 0, ComfortNoiseSupport: 0,
}, },
}, },
{ {
name: "116", name: "116",
value: "AgEAAAACAQI=", value: "AgEAAAACAQI=",
actual: &SupportedRTPConfig{}, actual: &SupportedRTPConfiguration{},
expect: &SupportedRTPConfig{ expect: &SupportedRTPConfiguration{
CryptoType: []byte{CryptoAES_CM_128_HMAC_SHA1_80, CryptoNone}, SRTPCryptoType: []byte{CryptoAES_CM_128_HMAC_SHA1_80, CryptoDisabled},
}, },
}, },
} }
+10 -10
View File
@@ -2,15 +2,15 @@ package camera
const TypeSupportedVideoStreamConfiguration = "114" const TypeSupportedVideoStreamConfiguration = "114"
type SupportedVideoStreamConfig struct { type SupportedVideoStreamConfiguration struct {
Codecs []VideoCodec `tlv8:"1"` Codecs []VideoCodecConfiguration `tlv8:"1"`
} }
type VideoCodec struct { type VideoCodecConfiguration struct {
CodecType byte `tlv8:"1"` CodecType byte `tlv8:"1"`
CodecParams []VideoParams `tlv8:"2"` CodecParams []VideoCodecParameters `tlv8:"2"`
VideoAttrs []VideoAttrs `tlv8:"3"` VideoAttrs []VideoCodecAttributes `tlv8:"3"`
RTPParams []RTPParams `tlv8:"4"` RTPParams []RTPParams `tlv8:"4"`
} }
//goland:noinspection ALL //goland:noinspection ALL
@@ -31,15 +31,15 @@ const (
VideoCodecCvoSuppported = 1 VideoCodecCvoSuppported = 1
) )
type VideoParams struct { type VideoCodecParameters struct {
ProfileID []byte `tlv8:"1"` // 0 - baseline, 1 - main, 2 - high ProfileID []byte `tlv8:"1"` // 0 - baseline, 1 - main, 2 - high
Level []byte `tlv8:"2"` // 0 - 3.1, 1 - 3.2, 2 - 4.0 Level []byte `tlv8:"2"` // 0 - 3.1, 1 - 3.2, 2 - 4.0
PacketizationMode byte `tlv8:"3"` // only 0 - non interleaved PacketizationMode byte `tlv8:"3"` // only 0 - non interleaved
CVOEnabled []byte `tlv8:"4"` // 0 - not supported, 1 - supported CVOEnabled []byte `tlv8:"4"` // 0 - not supported, 1 - supported
CVOID []byte `tlv8:"5"` // ??? CVOID []byte `tlv8:"5"` // ID for CVO RTP extensio
} }
type VideoAttrs struct { type VideoCodecAttributes struct {
Width uint16 `tlv8:"1"` Width uint16 `tlv8:"1"`
Height uint16 `tlv8:"2"` Height uint16 `tlv8:"2"`
Framerate uint8 `tlv8:"3"` Framerate uint8 `tlv8:"3"`
+13 -13
View File
@@ -2,9 +2,9 @@ package camera
const TypeSupportedAudioStreamConfiguration = "115" const TypeSupportedAudioStreamConfiguration = "115"
type SupportedAudioStreamConfig struct { type SupportedAudioStreamConfiguration struct {
Codecs []AudioCodec `tlv8:"1"` Codecs []AudioCodecConfiguration `tlv8:"1"`
ComfortNoise byte `tlv8:"2"` ComfortNoiseSupport byte `tlv8:"2"`
} }
//goland:noinspection ALL //goland:noinspection ALL
@@ -31,16 +31,16 @@ const (
RTPTimeAACLD24 = 40 // 24000/1000*40=960 RTPTimeAACLD24 = 40 // 24000/1000*40=960
) )
type AudioCodec struct { type AudioCodecConfiguration struct {
CodecType byte `tlv8:"1"` CodecType byte `tlv8:"1"`
CodecParams []AudioParams `tlv8:"2"` CodecParams []AudioCodecParameters `tlv8:"2"`
RTPParams []RTPParams `tlv8:"3"` RTPParams []RTPParams `tlv8:"3"`
ComfortNoise []byte `tlv8:"4"` ComfortNoise []byte `tlv8:"4"`
} }
type AudioParams struct { type AudioCodecParameters struct {
Channels uint8 `tlv8:"1"` Channels uint8 `tlv8:"1"`
Bitrate byte `tlv8:"2"` // 0 - variable, 1 - constant BitrateMode byte `tlv8:"2"` // 0 - variable, 1 - constant
SampleRate []byte `tlv8:"3"` // 0 - 8000, 1 - 16000, 2 - 24000 SampleRate []byte `tlv8:"3"` // 0 - 8000, 1 - 16000, 2 - 24000
RTPTime []uint8 `tlv8:"4"` // 20, 30, 40, 60 RTPTime []uint8 `tlv8:"4"` // 20, 30, 40, 60
} }
@@ -6,9 +6,9 @@ const TypeSupportedRTPConfiguration = "116"
const ( const (
CryptoAES_CM_128_HMAC_SHA1_80 = 0 CryptoAES_CM_128_HMAC_SHA1_80 = 0
CryptoAES_CM_256_HMAC_SHA1_80 = 1 CryptoAES_CM_256_HMAC_SHA1_80 = 1
CryptoNone = 2 CryptoDisabled = 2
) )
type SupportedRTPConfig struct { type SupportedRTPConfiguration struct {
CryptoType []byte `tlv8:"2"` SRTPCryptoType []byte `tlv8:"2"`
} }
+4 -4
View File
@@ -2,10 +2,10 @@ package camera
const TypeSelectedStreamConfiguration = "117" const TypeSelectedStreamConfiguration = "117"
type SelectedStreamConfig struct { type SelectedStreamConfiguration struct {
Control SessionControl `tlv8:"1"` Control SessionControl `tlv8:"1"`
VideoCodec VideoCodec `tlv8:"2"` VideoCodec VideoCodecConfiguration `tlv8:"2"`
AudioCodec AudioCodec `tlv8:"3"` AudioCodec AudioCodecConfiguration `tlv8:"3"`
} }
//goland:noinspection ALL //goland:noinspection ALL
+20 -13
View File
@@ -2,25 +2,32 @@ package camera
const TypeSetupEndpoints = "118" const TypeSetupEndpoints = "118"
type SetupEndpoints struct { type SetupEndpointsRequest struct {
SessionID string `tlv8:"1"` SessionID string `tlv8:"1"`
Status []byte `tlv8:"2"` Address Address `tlv8:"3"`
Address Addr `tlv8:"3"` VideoCrypto SRTPCryptoSuite `tlv8:"4"`
VideoCrypto CryptoSuite `tlv8:"4"` AudioCrypto SRTPCryptoSuite `tlv8:"5"`
AudioCrypto CryptoSuite `tlv8:"5"`
VideoSSRC []uint32 `tlv8:"6"`
AudioSSRC []uint32 `tlv8:"7"`
} }
type Addr struct { type SetupEndpointsResponse struct {
SessionID string `tlv8:"1"`
Status byte `tlv8:"2"`
Address Address `tlv8:"3"`
VideoCrypto SRTPCryptoSuite `tlv8:"4"`
AudioCrypto SRTPCryptoSuite `tlv8:"5"`
VideoSSRC uint32 `tlv8:"6"`
AudioSSRC uint32 `tlv8:"7"`
}
type Address struct {
IPVersion byte `tlv8:"1"` IPVersion byte `tlv8:"1"`
IPAddr string `tlv8:"2"` IPAddr string `tlv8:"2"`
VideoRTPPort uint16 `tlv8:"3"` VideoRTPPort uint16 `tlv8:"3"`
AudioRTPPort uint16 `tlv8:"4"` AudioRTPPort uint16 `tlv8:"4"`
} }
type CryptoSuite struct { type SRTPCryptoSuite struct {
CryptoType byte `tlv8:"1"` CryptoSuite byte `tlv8:"1"`
MasterKey string `tlv8:"2"` // 16 (AES_CM_128) or 32 (AES_256_CM) MasterKey string `tlv8:"2"` // 16 (AES_CM_128) or 32 (AES_256_CM)
MasterSalt string `tlv8:"3"` // 14 byte MasterSalt string `tlv8:"3"` // 14 byte
} }
+1 -1
View File
@@ -9,6 +9,6 @@ type StreamingStatus struct {
//goland:noinspection ALL //goland:noinspection ALL
const ( const (
StreamingStatusAvailable = 0 StreamingStatusAvailable = 0
StreamingStatusBusy = 1 StreamingStatusInUse = 1
StreamingStatusUnavailable = 2 StreamingStatusUnavailable = 2
) )
@@ -0,0 +1,11 @@
package camera
const TypeSupportedDataStreamTransportConfiguration = "130"
type SupportedDataStreamTransportConfiguration struct {
Configs []TransferTransportConfiguration `tlv8:"1"`
}
type TransferTransportConfiguration struct {
TransportType byte `tlv8:"1"`
}
+2 -2
View File
@@ -2,13 +2,13 @@ package camera
const TypeSetupDataStreamTransport = "131" const TypeSetupDataStreamTransport = "131"
type SetupDataStreamRequest struct { type SetupDataStreamTransportRequest struct {
SessionCommandType byte `tlv8:"1"` SessionCommandType byte `tlv8:"1"`
TransportType byte `tlv8:"2"` TransportType byte `tlv8:"2"`
ControllerKeySalt string `tlv8:"3"` ControllerKeySalt string `tlv8:"3"`
} }
type SetupDataStreamResponse struct { type SetupDataStreamTransportResponse struct {
Status byte `tlv8:"1"` Status byte `tlv8:"1"`
TransportTypeSessionParameters struct { TransportTypeSessionParameters struct {
TCPListeningPort uint16 `tlv8:"1"` TCPListeningPort uint16 `tlv8:"1"`
+18
View File
@@ -0,0 +1,18 @@
package camera
const TypeSupportedCameraRecordingConfiguration = "205"
type SupportedCameraRecordingConfiguration struct {
PrebufferLength uint32 `tlv8:"1"`
EventTriggerOptions uint64 `tlv8:"2"`
MediaContainerConfigurations `tlv8:"3"`
}
type MediaContainerConfigurations struct {
MediaContainerType uint8 `tlv8:"1"`
MediaContainerParameters `tlv8:"2"`
}
type MediaContainerParameters struct {
FragmentLength uint32 `tlv8:"1"`
}
+20
View File
@@ -0,0 +1,20 @@
package camera
const TypeSupportedVideoRecordingConfiguration = "206"
type SupportedVideoRecordingConfiguration struct {
CodecConfigs []VideoRecordingCodecConfiguration `tlv8:"1"`
}
type VideoRecordingCodecConfiguration struct {
CodecType uint8 `tlv8:"1"`
CodecParams VideoRecordingCodecParameters `tlv8:"2"`
CodecAttrs VideoCodecAttributes `tlv8:"3"`
}
type VideoRecordingCodecParameters struct {
ProfileID uint8 `tlv8:"1"`
Level uint8 `tlv8:"2"`
Bitrate uint32 `tlv8:"3"`
IFrameInterval uint32 `tlv8:"4"`
}
+19
View File
@@ -0,0 +1,19 @@
package camera
const TypeSupportedAudioRecordingConfiguration = "207"
type SupportedAudioRecordingConfiguration struct {
CodecConfigs []AudioRecordingCodecConfiguration `tlv8:"1"`
}
type AudioRecordingCodecConfiguration struct {
CodecType byte `tlv8:"1"`
CodecParams []AudioRecordingCodecParameters `tlv8:"2"`
}
type AudioRecordingCodecParameters struct {
Channels uint8 `tlv8:"1"`
BitrateMode []byte `tlv8:"2"`
SampleRate []byte `tlv8:"3"`
MaxAudioBitrate []uint32 `tlv8:"4"`
}
+9
View File
@@ -0,0 +1,9 @@
package camera
const TypeSelectedCameraRecordingConfiguration = "209"
type SelectedCameraRecordingConfiguration struct {
GeneralConfig SupportedCameraRecordingConfiguration `tlv8:"1"`
VideoConfig SupportedVideoRecordingConfiguration `tlv8:"2"`
AudioConfig SupportedAudioRecordingConfiguration `tlv8:"3"`
}
+11 -11
View File
@@ -15,7 +15,7 @@ type Stream struct {
} }
func NewStream( func NewStream(
client *hap.Client, videoCodec *VideoCodec, audioCodec *AudioCodec, client *hap.Client, videoCodec *VideoCodecConfiguration, audioCodec *AudioCodecConfiguration,
videoSession, audioSession *srtp.Session, bitrate int, videoSession, audioSession *srtp.Session, bitrate int,
) (*Stream, error) { ) (*Stream, error) {
stream := &Stream{ stream := &Stream{
@@ -58,7 +58,7 @@ func NewStream(
} }
audioCodec.ComfortNoise = []byte{0} audioCodec.ComfortNoise = []byte{0}
config := &SelectedStreamConfig{ config := &SelectedStreamConfiguration{
Control: SessionControl{ Control: SessionControl{
SessionID: stream.id, SessionID: stream.id,
Command: SessionCommandStart, Command: SessionCommandStart,
@@ -103,19 +103,19 @@ func (s *Stream) GetFreeStream() error {
} }
func (s *Stream) ExchangeEndpoints(videoSession, audioSession *srtp.Session) error { func (s *Stream) ExchangeEndpoints(videoSession, audioSession *srtp.Session) error {
req := SetupEndpoints{ req := SetupEndpointsRequest{
SessionID: s.id, SessionID: s.id,
Address: Addr{ Address: Address{
IPVersion: 0, IPVersion: 0,
IPAddr: videoSession.Local.Addr, IPAddr: videoSession.Local.Addr,
VideoRTPPort: videoSession.Local.Port, VideoRTPPort: videoSession.Local.Port,
AudioRTPPort: audioSession.Local.Port, AudioRTPPort: audioSession.Local.Port,
}, },
VideoCrypto: CryptoSuite{ VideoCrypto: SRTPCryptoSuite{
MasterKey: string(videoSession.Local.MasterKey), MasterKey: string(videoSession.Local.MasterKey),
MasterSalt: string(videoSession.Local.MasterSalt), MasterSalt: string(videoSession.Local.MasterSalt),
}, },
AudioCrypto: CryptoSuite{ AudioCrypto: SRTPCryptoSuite{
MasterKey: string(audioSession.Local.MasterKey), MasterKey: string(audioSession.Local.MasterKey),
MasterSalt: string(audioSession.Local.MasterSalt), MasterSalt: string(audioSession.Local.MasterSalt),
}, },
@@ -129,7 +129,7 @@ func (s *Stream) ExchangeEndpoints(videoSession, audioSession *srtp.Session) err
return err return err
} }
var res SetupEndpoints var res SetupEndpointsResponse
if err := s.client.GetCharacter(char); err != nil { if err := s.client.GetCharacter(char); err != nil {
return err return err
} }
@@ -142,7 +142,7 @@ func (s *Stream) ExchangeEndpoints(videoSession, audioSession *srtp.Session) err
Port: res.Address.VideoRTPPort, Port: res.Address.VideoRTPPort,
MasterKey: []byte(res.VideoCrypto.MasterKey), MasterKey: []byte(res.VideoCrypto.MasterKey),
MasterSalt: []byte(res.VideoCrypto.MasterSalt), MasterSalt: []byte(res.VideoCrypto.MasterSalt),
SSRC: res.VideoSSRC[0], SSRC: res.VideoSSRC,
} }
audioSession.Remote = &srtp.Endpoint{ audioSession.Remote = &srtp.Endpoint{
@@ -150,13 +150,13 @@ func (s *Stream) ExchangeEndpoints(videoSession, audioSession *srtp.Session) err
Port: res.Address.AudioRTPPort, Port: res.Address.AudioRTPPort,
MasterKey: []byte(res.AudioCrypto.MasterKey), MasterKey: []byte(res.AudioCrypto.MasterKey),
MasterSalt: []byte(res.AudioCrypto.MasterSalt), MasterSalt: []byte(res.AudioCrypto.MasterSalt),
SSRC: res.AudioSSRC[0], SSRC: res.AudioSSRC,
} }
return nil return nil
} }
func (s *Stream) SetStreamConfig(config *SelectedStreamConfig) error { func (s *Stream) SetStreamConfig(config *SelectedStreamConfiguration) error {
char := s.service.GetCharacter(TypeSelectedStreamConfiguration) char := s.service.GetCharacter(TypeSelectedStreamConfiguration)
if err := char.Write(config); err != nil { if err := char.Write(config); err != nil {
return err return err
@@ -169,7 +169,7 @@ func (s *Stream) SetStreamConfig(config *SelectedStreamConfig) error {
} }
func (s *Stream) Close() error { func (s *Stream) Close() error {
config := &SelectedStreamConfig{ config := &SelectedStreamConfiguration{
Control: SessionControl{ Control: SessionControl{
SessionID: s.id, SessionID: s.id,
Command: SessionCommandEnd, Command: SessionCommandEnd,
+10 -10
View File
@@ -59,7 +59,7 @@ func NewConsumer(conn net.Conn, server *srtp.Server) *Consumer {
} }
} }
func (c *Consumer) SetOffer(offer *camera.SetupEndpoints) { func (c *Consumer) SetOffer(offer *camera.SetupEndpointsRequest) {
c.sessionID = offer.SessionID c.sessionID = offer.SessionID
c.videoSession = &srtp.Session{ c.videoSession = &srtp.Session{
Remote: &srtp.Endpoint{ Remote: &srtp.Endpoint{
@@ -79,32 +79,32 @@ func (c *Consumer) SetOffer(offer *camera.SetupEndpoints) {
} }
} }
func (c *Consumer) GetAnswer() *camera.SetupEndpoints { func (c *Consumer) GetAnswer() *camera.SetupEndpointsResponse {
c.videoSession.Local = c.srtpEndpoint() c.videoSession.Local = c.srtpEndpoint()
c.audioSession.Local = c.srtpEndpoint() c.audioSession.Local = c.srtpEndpoint()
return &camera.SetupEndpoints{ return &camera.SetupEndpointsResponse{
SessionID: c.sessionID, SessionID: c.sessionID,
Status: []byte{0}, Status: camera.StreamingStatusAvailable,
Address: camera.Addr{ Address: camera.Address{
IPAddr: c.videoSession.Local.Addr, IPAddr: c.videoSession.Local.Addr,
VideoRTPPort: c.videoSession.Local.Port, VideoRTPPort: c.videoSession.Local.Port,
AudioRTPPort: c.audioSession.Local.Port, AudioRTPPort: c.audioSession.Local.Port,
}, },
VideoCrypto: camera.CryptoSuite{ VideoCrypto: camera.SRTPCryptoSuite{
MasterKey: string(c.videoSession.Local.MasterKey), MasterKey: string(c.videoSession.Local.MasterKey),
MasterSalt: string(c.videoSession.Local.MasterSalt), MasterSalt: string(c.videoSession.Local.MasterSalt),
}, },
AudioCrypto: camera.CryptoSuite{ AudioCrypto: camera.SRTPCryptoSuite{
MasterKey: string(c.audioSession.Local.MasterKey), MasterKey: string(c.audioSession.Local.MasterKey),
MasterSalt: string(c.audioSession.Local.MasterSalt), MasterSalt: string(c.audioSession.Local.MasterSalt),
}, },
VideoSSRC: []uint32{c.videoSession.Local.SSRC}, VideoSSRC: c.videoSession.Local.SSRC,
AudioSSRC: []uint32{c.audioSession.Local.SSRC}, AudioSSRC: c.audioSession.Local.SSRC,
} }
} }
func (c *Consumer) SetConfig(conf *camera.SelectedStreamConfig) bool { func (c *Consumer) SetConfig(conf *camera.SelectedStreamConfiguration) bool {
if c.sessionID != conf.Control.SessionID { if c.sessionID != conf.Control.SessionID {
return false return false
} }
+9 -9
View File
@@ -13,7 +13,7 @@ var videoCodecs = [...]string{core.CodecH264}
var videoProfiles = [...]string{"4200", "4D00", "6400"} var videoProfiles = [...]string{"4200", "4D00", "6400"}
var videoLevels = [...]string{"1F", "20", "28"} var videoLevels = [...]string{"1F", "20", "28"}
func videoToMedia(codecs []camera.VideoCodec) *core.Media { func videoToMedia(codecs []camera.VideoCodecConfiguration) *core.Media {
media := &core.Media{ media := &core.Media{
Kind: core.KindVideo, Direction: core.DirectionRecvonly, Kind: core.KindVideo, Direction: core.DirectionRecvonly,
} }
@@ -39,7 +39,7 @@ func videoToMedia(codecs []camera.VideoCodec) *core.Media {
var audioCodecs = [...]string{core.CodecPCMU, core.CodecPCMA, core.CodecELD, core.CodecOpus} var audioCodecs = [...]string{core.CodecPCMU, core.CodecPCMA, core.CodecELD, core.CodecOpus}
var audioSampleRates = [...]uint32{8000, 16000, 24000} var audioSampleRates = [...]uint32{8000, 16000, 24000}
func audioToMedia(codecs []camera.AudioCodec) *core.Media { func audioToMedia(codecs []camera.AudioCodecConfiguration) *core.Media {
media := &core.Media{ media := &core.Media{
Kind: core.KindAudio, Direction: core.DirectionRecvonly, Kind: core.KindAudio, Direction: core.DirectionRecvonly,
} }
@@ -67,7 +67,7 @@ func audioToMedia(codecs []camera.AudioCodec) *core.Media {
return media return media
} }
func trackToVideo(track *core.Receiver, video0 *camera.VideoCodec) *camera.VideoCodec { func trackToVideo(track *core.Receiver, video0 *camera.VideoCodecConfiguration) *camera.VideoCodecConfiguration {
profileID := video0.CodecParams[0].ProfileID[0] profileID := video0.CodecParams[0].ProfileID[0]
level := video0.CodecParams[0].Level[0] level := video0.CodecParams[0].Level[0]
attrs := video0.VideoAttrs[0] attrs := video0.VideoAttrs[0]
@@ -96,19 +96,19 @@ func trackToVideo(track *core.Receiver, video0 *camera.VideoCodec) *camera.Video
} }
} }
return &camera.VideoCodec{ return &camera.VideoCodecConfiguration{
CodecType: video0.CodecType, CodecType: video0.CodecType,
CodecParams: []camera.VideoParams{ CodecParams: []camera.VideoCodecParameters{
{ {
ProfileID: []byte{profileID}, ProfileID: []byte{profileID},
Level: []byte{level}, Level: []byte{level},
}, },
}, },
VideoAttrs: []camera.VideoAttrs{attrs}, VideoAttrs: []camera.VideoCodecAttributes{attrs},
} }
} }
func trackToAudio(track *core.Receiver, audio0 *camera.AudioCodec) *camera.AudioCodec { func trackToAudio(track *core.Receiver, audio0 *camera.AudioCodecConfiguration) *camera.AudioCodecConfiguration {
codecType := audio0.CodecType codecType := audio0.CodecType
channels := audio0.CodecParams[0].Channels channels := audio0.CodecParams[0].Channels
sampleRate := audio0.CodecParams[0].SampleRate[0] sampleRate := audio0.CodecParams[0].SampleRate[0]
@@ -131,9 +131,9 @@ func trackToAudio(track *core.Receiver, audio0 *camera.AudioCodec) *camera.Audio
} }
} }
return &camera.AudioCodec{ return &camera.AudioCodecConfiguration{
CodecType: codecType, CodecType: codecType,
CodecParams: []camera.AudioParams{ CodecParams: []camera.AudioCodecParameters{
{ {
Channels: channels, Channels: channels,
SampleRate: []byte{sampleRate}, SampleRate: []byte{sampleRate},
+2 -2
View File
@@ -22,8 +22,8 @@ type Client struct {
hap *hap.Client hap *hap.Client
srtp *srtp.Server srtp *srtp.Server
videoConfig camera.SupportedVideoStreamConfig videoConfig camera.SupportedVideoStreamConfiguration
audioConfig camera.SupportedAudioStreamConfig audioConfig camera.SupportedAudioStreamConfiguration
videoSession *srtp.Session videoSession *srtp.Session
audioSession *srtp.Session audioSession *srtp.Session
+2 -2
View File
@@ -74,7 +74,7 @@ func (p *Proxy) handleCon(pair ServerPair) error {
_ = json.Unmarshal(body, &v) _ = json.Unmarshal(body, &v)
for _, char := range v.Value { for _, char := range v.Value {
if char.IID == hdsCharIID { if char.IID == hdsCharIID {
var hdsReq camera.SetupDataStreamRequest var hdsReq camera.SetupDataStreamTransportRequest
_ = tlv8.UnmarshalBase64(char.Value, &hdsReq) _ = tlv8.UnmarshalBase64(char.Value, &hdsReq)
hdsConSalt = hdsReq.ControllerKeySalt hdsConSalt = hdsReq.ControllerKeySalt
break break
@@ -110,7 +110,7 @@ func (p *Proxy) handleCon(pair ServerPair) error {
_ = json.Unmarshal(body, &v) _ = json.Unmarshal(body, &v)
for i, char := range v.Value { for i, char := range v.Value {
if char.IID == hdsCharIID { if char.IID == hdsCharIID {
var hdsRes camera.SetupDataStreamResponse var hdsRes camera.SetupDataStreamTransportResponse
_ = tlv8.UnmarshalBase64(char.Value, &hdsRes) _ = tlv8.UnmarshalBase64(char.Value, &hdsRes)
hdsAccSalt := hdsRes.AccessoryKeySalt hdsAccSalt := hdsRes.AccessoryKeySalt