1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14#[derive(Debug, serde::Serialize)]
17pub struct SFrame {
18 pub cipher_suite: Option<u16>,
19 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
20 pub base_key: Option<Vec<u8>>,
21 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
22 pub kid: Option<Vec<u8>>,
23}
24
25pub struct SolicitOfferParams {
29 pub stream_usage: u8,
30 pub originating_endpoint_id: u16,
31 pub video_stream_id: Option<u8>,
32 pub audio_stream_id: Option<u8>,
33 pub ice_transport_policy: String,
34 pub metadata_enabled: bool,
35 pub s_frame_config: SFrame,
36 pub video_streams: Vec<u8>,
37 pub audio_streams: Vec<u8>,
38}
39
40pub fn encode_solicit_offer(params: SolicitOfferParams) -> anyhow::Result<Vec<u8>> {
42 let mut s_frame_config_fields = Vec::new();
44 if let Some(x) = params.s_frame_config.cipher_suite { s_frame_config_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
45 if let Some(x) = params.s_frame_config.base_key { s_frame_config_fields.push((1, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
46 if let Some(x) = params.s_frame_config.kid { s_frame_config_fields.push((2, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
47 let tlv = tlv::TlvItemEnc {
48 tag: 0,
49 value: tlv::TlvItemValueEnc::StructInvisible(vec![
50 (0, tlv::TlvItemValueEnc::UInt8(params.stream_usage)).into(),
51 (1, tlv::TlvItemValueEnc::UInt16(params.originating_endpoint_id)).into(),
52 (2, tlv::TlvItemValueEnc::UInt8(params.video_stream_id.unwrap_or(0))).into(),
53 (3, tlv::TlvItemValueEnc::UInt8(params.audio_stream_id.unwrap_or(0))).into(),
54 (5, tlv::TlvItemValueEnc::String(params.ice_transport_policy)).into(),
55 (6, tlv::TlvItemValueEnc::Bool(params.metadata_enabled)).into(),
56 (7, tlv::TlvItemValueEnc::StructInvisible(s_frame_config_fields)).into(),
57 (8, tlv::TlvItemValueEnc::StructAnon(params.video_streams.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
58 (9, tlv::TlvItemValueEnc::StructAnon(params.audio_streams.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
59 ]),
60 };
61 Ok(tlv.encode()?)
62}
63
64pub struct ProvideOfferParams {
66 pub web_rtc_session_id: Option<u8>,
67 pub sdp: String,
68 pub stream_usage: u8,
69 pub originating_endpoint_id: u16,
70 pub video_stream_id: Option<u8>,
71 pub audio_stream_id: Option<u8>,
72 pub ice_transport_policy: String,
73 pub metadata_enabled: bool,
74 pub s_frame_config: SFrame,
75 pub video_streams: Vec<u8>,
76 pub audio_streams: Vec<u8>,
77}
78
79pub fn encode_provide_offer(params: ProvideOfferParams) -> anyhow::Result<Vec<u8>> {
81 let mut s_frame_config_fields = Vec::new();
83 if let Some(x) = params.s_frame_config.cipher_suite { s_frame_config_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
84 if let Some(x) = params.s_frame_config.base_key { s_frame_config_fields.push((1, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
85 if let Some(x) = params.s_frame_config.kid { s_frame_config_fields.push((2, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
86 let tlv = tlv::TlvItemEnc {
87 tag: 0,
88 value: tlv::TlvItemValueEnc::StructInvisible(vec![
89 (0, tlv::TlvItemValueEnc::UInt8(params.web_rtc_session_id.unwrap_or(0))).into(),
90 (1, tlv::TlvItemValueEnc::String(params.sdp)).into(),
91 (2, tlv::TlvItemValueEnc::UInt8(params.stream_usage)).into(),
92 (3, tlv::TlvItemValueEnc::UInt16(params.originating_endpoint_id)).into(),
93 (4, tlv::TlvItemValueEnc::UInt8(params.video_stream_id.unwrap_or(0))).into(),
94 (5, tlv::TlvItemValueEnc::UInt8(params.audio_stream_id.unwrap_or(0))).into(),
95 (7, tlv::TlvItemValueEnc::String(params.ice_transport_policy)).into(),
96 (8, tlv::TlvItemValueEnc::Bool(params.metadata_enabled)).into(),
97 (9, tlv::TlvItemValueEnc::StructInvisible(s_frame_config_fields)).into(),
98 (10, tlv::TlvItemValueEnc::StructAnon(params.video_streams.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
99 (11, tlv::TlvItemValueEnc::StructAnon(params.audio_streams.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
100 ]),
101 };
102 Ok(tlv.encode()?)
103}
104
105pub fn encode_provide_answer(web_rtc_session_id: u8, sdp: String) -> anyhow::Result<Vec<u8>> {
107 let tlv = tlv::TlvItemEnc {
108 tag: 0,
109 value: tlv::TlvItemValueEnc::StructInvisible(vec![
110 (0, tlv::TlvItemValueEnc::UInt8(web_rtc_session_id)).into(),
111 (1, tlv::TlvItemValueEnc::String(sdp)).into(),
112 ]),
113 };
114 Ok(tlv.encode()?)
115}
116
117pub fn encode_provide_ice_candidates(web_rtc_session_id: u8) -> anyhow::Result<Vec<u8>> {
119 let tlv = tlv::TlvItemEnc {
120 tag: 0,
121 value: tlv::TlvItemValueEnc::StructInvisible(vec![
122 (0, tlv::TlvItemValueEnc::UInt8(web_rtc_session_id)).into(),
123 ]),
124 };
125 Ok(tlv.encode()?)
126}
127
128pub fn encode_end_session(web_rtc_session_id: u8, reason: u8) -> anyhow::Result<Vec<u8>> {
130 let tlv = tlv::TlvItemEnc {
131 tag: 0,
132 value: tlv::TlvItemValueEnc::StructInvisible(vec![
133 (0, tlv::TlvItemValueEnc::UInt8(web_rtc_session_id)).into(),
134 (1, tlv::TlvItemValueEnc::UInt8(reason)).into(),
135 ]),
136 };
137 Ok(tlv.encode()?)
138}
139
140pub fn decode_current_sessions(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u8>> {
144 let mut res = Vec::new();
145 if let tlv::TlvItemValue::List(v) = inp {
146 for item in v {
147 if let tlv::TlvItemValue::Int(i) = &item.value {
148 res.push(*i as u8);
149 }
150 }
151 }
152 Ok(res)
153}
154
155
156pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
168 if cluster_id != 0x0553 {
170 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0553, got {}\"}}", cluster_id);
171 }
172
173 match attribute_id {
174 0x0000 => {
175 match decode_current_sessions(tlv_value) {
176 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
177 Err(e) => format!("{{\"error\": \"{}\"}}", e),
178 }
179 }
180 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
181 }
182}
183
184pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
189 vec![
190 (0x0000, "CurrentSessions"),
191 ]
192}
193
194#[derive(Debug, serde::Serialize)]
195pub struct SolicitOfferResponse {
196 pub web_rtc_session_id: Option<u8>,
197 pub deferred_offer: Option<bool>,
198 pub video_stream_id: Option<u8>,
199 pub audio_stream_id: Option<u8>,
200}
201
202#[derive(Debug, serde::Serialize)]
203pub struct ProvideOfferResponse {
204 pub web_rtc_session_id: Option<u8>,
205 pub video_stream_id: Option<u8>,
206 pub audio_stream_id: Option<u8>,
207}
208
209pub fn decode_solicit_offer_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SolicitOfferResponse> {
213 if let tlv::TlvItemValue::List(_fields) = inp {
214 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
215 Ok(SolicitOfferResponse {
216 web_rtc_session_id: item.get_int(&[0]).map(|v| v as u8),
217 deferred_offer: item.get_bool(&[1]),
218 video_stream_id: item.get_int(&[2]).map(|v| v as u8),
219 audio_stream_id: item.get_int(&[3]).map(|v| v as u8),
220 })
221 } else {
222 Err(anyhow::anyhow!("Expected struct fields"))
223 }
224}
225
226pub fn decode_provide_offer_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ProvideOfferResponse> {
228 if let tlv::TlvItemValue::List(_fields) = inp {
229 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
230 Ok(ProvideOfferResponse {
231 web_rtc_session_id: item.get_int(&[0]).map(|v| v as u8),
232 video_stream_id: item.get_int(&[1]).map(|v| v as u8),
233 audio_stream_id: item.get_int(&[2]).map(|v| v as u8),
234 })
235 } else {
236 Err(anyhow::anyhow!("Expected struct fields"))
237 }
238}
239