implement skill handling and media configuration

This commit is contained in:
seydx
2025-05-11 00:51:17 +02:00
parent e74fc6f198
commit 30d48e139c
3 changed files with 101 additions and 40 deletions
+91 -6
View File
@@ -31,6 +31,7 @@ type TuyaClient struct {
motoID string
auth string
iceServers []pionWebrtc.ICEServer
medias []*core.Media
}
type Token struct {
@@ -41,8 +42,8 @@ type Token struct {
}
type AudioAttributes struct {
CallMode []int `json:"call_mode"`
HardwareCapability []int `json:"hardware_capability"`
CallMode []int `json:"call_mode"` // 1 = one way, 2 = two way
HardwareCapability []int `json:"hardware_capability"` // 1 = mic, 2 = speaker
}
type OpenApiICE struct {
@@ -62,6 +63,24 @@ type P2PConfig struct {
Ices []OpenApiICE `json:"ices"`
}
type Skill struct {
WebRTC int `json:"webrtc"`
Audios []struct {
Channels int `json:"channels"`
DataBit int `json:"dataBit"`
CodecType int `json:"codecType"`
SampleRate int `json:"sampleRate"`
} `json:"audios"`
Videos []struct {
StreamType int `json:"streamType"` // streamType = 2 => H265 and streamType = 4 => H264
ProfileId string `json:"profileId"`
Width int `json:"width"`
CodecType int `json:"codecType"`
SampleRate int `json:"sampleRate"`
Height int `json:"height"`
} `json:"videos"`
}
type WebRTConfig struct {
AudioAttributes AudioAttributes `json:"audio_attributes"`
Auth string `json:"auth"`
@@ -73,14 +92,14 @@ type WebRTConfig struct {
VideoClaritiy int `json:"video_clarity"`
}
type TokenResponse struct {
Result Token `json:"result"`
}
type WebRTCConfigResponse struct {
Result WebRTConfig `json:"result"`
}
type TokenResponse struct {
Result Token `json:"result"`
}
type OpenIoTHubConfigRequest struct {
UID string `json:"uid"`
UniqueID string `json:"unique_id"`
@@ -234,6 +253,63 @@ func(c *TuyaClient) InitDevice() (err error) {
c.motoID = webRTCConfigResponse.Result.MotoID
c.auth = webRTCConfigResponse.Result.Auth
var skill Skill
err = json.Unmarshal([]byte(webRTCConfigResponse.Result.Skill), &skill)
if err != nil {
return fmt.Errorf("failed to unmarshal skill: %w", err)
}
var audioDirection string
if contains(webRTCConfigResponse.Result.AudioAttributes.CallMode, 2) && contains(webRTCConfigResponse.Result.AudioAttributes.HardwareCapability, 1) {
audioDirection = core.DirectionSendRecv
} else {
audioDirection = core.DirectionRecvonly
}
c.medias = make([]*core.Media, 0)
for _, audio := range skill.Audios {
media := &core.Media{
Kind: core.KindAudio,
Direction: audioDirection,
Codecs: []*core.Codec{
{
Name: "PCMU",
ClockRate: uint32(audio.SampleRate),
Channels: uint8(audio.Channels),
},
},
}
c.medias = append(c.medias, media)
}
// take only the first video codec
video := skill.Videos[0]
var name string
switch video.CodecType {
case 4:
name = core.CodecH265
case 2:
name = core.CodecH264
default:
name = core.CodecH264
}
media := &core.Media{
Kind: core.KindVideo,
Direction: core.DirectionRecvonly,
Codecs: []*core.Codec{
{
Name: name,
ClockRate: uint32(video.SampleRate),
PayloadType: 96,
},
},
}
c.medias = append(c.medias, media)
iceServersBytes, err := json.Marshal(&webRTCConfigResponse.Result.P2PConfig.Ices)
if err != nil {
return fmt.Errorf("failed to marshal ICE servers: %w", err)
@@ -283,3 +359,12 @@ func(c *TuyaClient) calBusinessSign(ts int64) string {
res := fmt.Sprintf("%X", val)
return res
}
func contains(slice []int, val int) bool {
for _, item := range slice {
if item == val {
return true
}
}
return false
}
+6 -30
View File
@@ -58,8 +58,8 @@ func Dial(rawURL string) (*Client, error) {
conf := pion.Configuration{
ICEServers: client.api.iceServers,
// ICETransportPolicy: pion.ICETransportPolicyAll,
// BundlePolicy: pion.BundlePolicyMaxBundle,
ICETransportPolicy: pion.ICETransportPolicyAll,
BundlePolicy: pion.BundlePolicyMaxBundle,
}
api, err := webrtc.NewAPI()
@@ -99,12 +99,13 @@ func Dial(rawURL string) (*Client, error) {
}
if err = pc.SetRemoteDescription(desc); err != nil {
client.Stop()
return
}
prod.SetAnswer(answer.Sdp)
if err != nil {
if err = prod.SetAnswer(answer.Sdp); err != nil {
client.Stop()
return
}
prod.SDP = answer.Sdp
@@ -148,32 +149,8 @@ func Dial(rawURL string) (*Client, error) {
}
})
medias := []*core.Media{
{
Kind: core.KindAudio,
Direction: core.DirectionSendRecv,
Codecs: []*core.Codec{
{
Name: "PCMU",
ClockRate: 8000,
Channels: 1,
},
},
},
{
Kind: core.KindVideo,
Direction: core.DirectionRecvonly,
Codecs: []*core.Codec{
{
Name: "H264",
ClockRate: 90000,
},
},
},
}
// Create offer
offer, err := prod.CreateOffer(medias)
offer, err := prod.CreateOffer(client.api.medias)
if err != nil {
client.api.Close()
return nil, err
@@ -231,7 +208,6 @@ func (c *Client) Stop() error {
if c.api != nil {
c.api.Close()
c.api = nil
}
return nil
+2 -2
View File
@@ -63,12 +63,12 @@ func (c *Conn) SetAnswer(answer string) (err error) {
SDP: fakeFormatsInAnswer(c.pc.LocalDescription().SDP, answer),
}
if err = c.pc.SetRemoteDescription(desc); err != nil {
return
return err
}
sd := &sdp.SessionDescription{}
if err = sd.Unmarshal([]byte(answer)); err != nil {
return
return err
}
c.Medias = UnmarshalMedias(sd.MediaDescriptions)