1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum CMAFInterface {
19 Interface1 = 0,
21 Interface2dash = 1,
23 Interface2hls = 2,
25}
26
27impl CMAFInterface {
28 pub fn from_u8(value: u8) -> Option<Self> {
30 match value {
31 0 => Some(CMAFInterface::Interface1),
32 1 => Some(CMAFInterface::Interface2dash),
33 2 => Some(CMAFInterface::Interface2hls),
34 _ => None,
35 }
36 }
37
38 pub fn to_u8(self) -> u8 {
40 self as u8
41 }
42}
43
44impl From<CMAFInterface> for u8 {
45 fn from(val: CMAFInterface) -> Self {
46 val as u8
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
51#[repr(u8)]
52pub enum ContainerFormat {
53 Cmaf = 0,
55}
56
57impl ContainerFormat {
58 pub fn from_u8(value: u8) -> Option<Self> {
60 match value {
61 0 => Some(ContainerFormat::Cmaf),
62 _ => None,
63 }
64 }
65
66 pub fn to_u8(self) -> u8 {
68 self as u8
69 }
70}
71
72impl From<ContainerFormat> for u8 {
73 fn from(val: ContainerFormat) -> Self {
74 val as u8
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
79#[repr(u8)]
80pub enum IngestMethods {
81 Cmafingest = 0,
83}
84
85impl IngestMethods {
86 pub fn from_u8(value: u8) -> Option<Self> {
88 match value {
89 0 => Some(IngestMethods::Cmafingest),
90 _ => None,
91 }
92 }
93
94 pub fn to_u8(self) -> u8 {
96 self as u8
97 }
98}
99
100impl From<IngestMethods> for u8 {
101 fn from(val: IngestMethods) -> Self {
102 val as u8
103 }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
107#[repr(u8)]
108pub enum StatusCode {
109 Invalidtlsendpoint = 2,
111 Invalidstream = 3,
113 Invalidurl = 4,
115 Invalidzone = 5,
117 Invalidcombination = 6,
119 Invalidtriggertype = 7,
121 Invalidtransportstatus = 8,
123 Invalidoptions = 9,
125 Invalidstreamusage = 10,
127 Invalidtime = 11,
129 Invalidprerolllength = 12,
131 Duplicatestreamvalues = 13,
133}
134
135impl StatusCode {
136 pub fn from_u8(value: u8) -> Option<Self> {
138 match value {
139 2 => Some(StatusCode::Invalidtlsendpoint),
140 3 => Some(StatusCode::Invalidstream),
141 4 => Some(StatusCode::Invalidurl),
142 5 => Some(StatusCode::Invalidzone),
143 6 => Some(StatusCode::Invalidcombination),
144 7 => Some(StatusCode::Invalidtriggertype),
145 8 => Some(StatusCode::Invalidtransportstatus),
146 9 => Some(StatusCode::Invalidoptions),
147 10 => Some(StatusCode::Invalidstreamusage),
148 11 => Some(StatusCode::Invalidtime),
149 12 => Some(StatusCode::Invalidprerolllength),
150 13 => Some(StatusCode::Duplicatestreamvalues),
151 _ => None,
152 }
153 }
154
155 pub fn to_u8(self) -> u8 {
157 self as u8
158 }
159}
160
161impl From<StatusCode> for u8 {
162 fn from(val: StatusCode) -> Self {
163 val as u8
164 }
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
168#[repr(u8)]
169pub enum TransportStatus {
170 Active = 0,
172 Inactive = 1,
174}
175
176impl TransportStatus {
177 pub fn from_u8(value: u8) -> Option<Self> {
179 match value {
180 0 => Some(TransportStatus::Active),
181 1 => Some(TransportStatus::Inactive),
182 _ => None,
183 }
184 }
185
186 pub fn to_u8(self) -> u8 {
188 self as u8
189 }
190}
191
192impl From<TransportStatus> for u8 {
193 fn from(val: TransportStatus) -> Self {
194 val as u8
195 }
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
199#[repr(u8)]
200pub enum TransportTriggerType {
201 Command = 0,
203 Motion = 1,
205 Continuous = 2,
207}
208
209impl TransportTriggerType {
210 pub fn from_u8(value: u8) -> Option<Self> {
212 match value {
213 0 => Some(TransportTriggerType::Command),
214 1 => Some(TransportTriggerType::Motion),
215 2 => Some(TransportTriggerType::Continuous),
216 _ => None,
217 }
218 }
219
220 pub fn to_u8(self) -> u8 {
222 self as u8
223 }
224}
225
226impl From<TransportTriggerType> for u8 {
227 fn from(val: TransportTriggerType) -> Self {
228 val as u8
229 }
230}
231
232#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
233#[repr(u8)]
234pub enum TriggerActivationReason {
235 Userinitiated = 0,
237 Automation = 1,
239 Emergency = 2,
241 Doorbellpressed = 3,
243}
244
245impl TriggerActivationReason {
246 pub fn from_u8(value: u8) -> Option<Self> {
248 match value {
249 0 => Some(TriggerActivationReason::Userinitiated),
250 1 => Some(TriggerActivationReason::Automation),
251 2 => Some(TriggerActivationReason::Emergency),
252 3 => Some(TriggerActivationReason::Doorbellpressed),
253 _ => None,
254 }
255 }
256
257 pub fn to_u8(self) -> u8 {
259 self as u8
260 }
261}
262
263impl From<TriggerActivationReason> for u8 {
264 fn from(val: TriggerActivationReason) -> Self {
265 val as u8
266 }
267}
268
269#[derive(Debug, serde::Serialize)]
272pub struct AudioStream {
273 pub audio_stream_name: Option<String>,
274 pub audio_stream_id: Option<u8>,
275}
276
277#[derive(Debug, serde::Serialize)]
278pub struct CMAFContainerOptions {
279 pub cmaf_interface: Option<CMAFInterface>,
280 pub segment_duration: Option<u16>,
281 pub chunk_duration: Option<u16>,
282 pub session_group: Option<u8>,
283 pub track_name: Option<String>,
284 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
285 pub cenc_key: Option<Vec<u8>>,
286 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
287 pub cenc_key_id: Option<Vec<u8>>,
288 pub metadata_enabled: Option<bool>,
289}
290
291#[derive(Debug, serde::Serialize)]
292pub struct ContainerOptions {
293 pub container_type: Option<ContainerFormat>,
294 pub cmaf_container_options: Option<CMAFContainerOptions>,
295}
296
297#[derive(Debug, serde::Serialize)]
298pub struct SupportedFormat {
299 pub container_format: Option<ContainerFormat>,
300 pub ingest_method: Option<IngestMethods>,
301}
302
303#[derive(Debug, serde::Serialize)]
304pub struct TransportConfiguration {
305 pub connection_id: Option<u8>,
306 pub transport_status: Option<TransportStatus>,
307 pub transport_options: Option<TransportOptions>,
308}
309
310#[derive(Debug, serde::Serialize)]
311pub struct TransportMotionTriggerTimeControl {
312 pub initial_duration: Option<u16>,
313 pub augmentation_duration: Option<u16>,
314 pub max_duration: Option<u32>,
315 pub blind_duration: Option<u16>,
316}
317
318#[derive(Debug, serde::Serialize)]
319pub struct TransportOptions {
320 pub stream_usage: Option<u8>,
321 pub video_stream_id: Option<u8>,
322 pub audio_stream_id: Option<u8>,
323 pub tls_endpoint_id: Option<u8>,
324 pub url: Option<String>,
325 pub trigger_options: Option<TransportTriggerOptions>,
326 pub ingest_method: Option<IngestMethods>,
327 pub container_options: Option<ContainerOptions>,
328 pub expiry_time: Option<u64>,
329 pub video_streams: Option<Vec<VideoStream>>,
330 pub audio_streams: Option<Vec<AudioStream>>,
331}
332
333#[derive(Debug, serde::Serialize)]
334pub struct TransportTriggerOptions {
335 pub trigger_type: Option<TransportTriggerType>,
336 pub motion_zones: Option<Vec<TransportZoneOptions>>,
337 pub motion_sensitivity: Option<u8>,
338 pub motion_time_control: Option<TransportMotionTriggerTimeControl>,
339 pub max_pre_roll_len: Option<u16>,
340}
341
342#[derive(Debug, serde::Serialize)]
343pub struct TransportZoneOptions {
344 pub zone: Option<u8>,
345 pub sensitivity: Option<u8>,
346}
347
348#[derive(Debug, serde::Serialize)]
349pub struct VideoStream {
350 pub video_stream_name: Option<String>,
351 pub video_stream_id: Option<u8>,
352}
353
354pub fn encode_allocate_push_transport(transport_options: TransportOptions) -> anyhow::Result<Vec<u8>> {
358 let mut transport_options_fields = Vec::new();
360 if let Some(x) = transport_options.stream_usage { transport_options_fields.push((0, tlv::TlvItemValueEnc::UInt8(x)).into()); }
361 if let Some(x) = transport_options.url { transport_options_fields.push((4, tlv::TlvItemValueEnc::String(x.clone())).into()); }
365 if let Some(inner) = transport_options.trigger_options {
366 let mut trigger_options_nested_fields = Vec::new();
367 if let Some(x) = inner.trigger_type { trigger_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
368 if let Some(listv) = inner.motion_zones {
369 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
370 let mut nested_fields = Vec::new();
371 if let Some(x) = inner.sensitivity { nested_fields.push((1, tlv::TlvItemValueEnc::UInt8(x)).into()); }
373 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
374 }).collect();
375 trigger_options_nested_fields.push((1, tlv::TlvItemValueEnc::Array(inner_vec)).into());
376 }
377 if let Some(x) = inner.motion_sensitivity { trigger_options_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x)).into()); }
378 if let Some(inner) = inner.motion_time_control {
379 let mut motion_time_control_nested_fields = Vec::new();
380 if let Some(x) = inner.initial_duration { motion_time_control_nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
381 if let Some(x) = inner.augmentation_duration { motion_time_control_nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
382 if let Some(x) = inner.max_duration { motion_time_control_nested_fields.push((2, tlv::TlvItemValueEnc::UInt32(x)).into()); }
383 if let Some(x) = inner.blind_duration { motion_time_control_nested_fields.push((3, tlv::TlvItemValueEnc::UInt16(x)).into()); }
384 trigger_options_nested_fields.push((3, tlv::TlvItemValueEnc::StructInvisible(motion_time_control_nested_fields)).into());
385 }
386 if let Some(x) = inner.max_pre_roll_len { trigger_options_nested_fields.push((4, tlv::TlvItemValueEnc::UInt16(x)).into()); }
387 transport_options_fields.push((5, tlv::TlvItemValueEnc::StructInvisible(trigger_options_nested_fields)).into());
388 }
389 if let Some(x) = transport_options.ingest_method { transport_options_fields.push((6, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
390 if let Some(inner) = transport_options.container_options {
391 let mut container_options_nested_fields = Vec::new();
392 if let Some(x) = inner.container_type { container_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
393 if let Some(inner) = inner.cmaf_container_options {
394 let mut cmaf_container_options_nested_fields = Vec::new();
395 if let Some(x) = inner.cmaf_interface { cmaf_container_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
396 if let Some(x) = inner.segment_duration { cmaf_container_options_nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
397 if let Some(x) = inner.chunk_duration { cmaf_container_options_nested_fields.push((2, tlv::TlvItemValueEnc::UInt16(x)).into()); }
398 if let Some(x) = inner.session_group { cmaf_container_options_nested_fields.push((3, tlv::TlvItemValueEnc::UInt8(x)).into()); }
399 if let Some(x) = inner.track_name { cmaf_container_options_nested_fields.push((4, tlv::TlvItemValueEnc::String(x.clone())).into()); }
400 if let Some(x) = inner.cenc_key { cmaf_container_options_nested_fields.push((5, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
401 if let Some(x) = inner.cenc_key_id { cmaf_container_options_nested_fields.push((6, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
402 if let Some(x) = inner.metadata_enabled { cmaf_container_options_nested_fields.push((7, tlv::TlvItemValueEnc::Bool(x)).into()); }
403 container_options_nested_fields.push((1, tlv::TlvItemValueEnc::StructInvisible(cmaf_container_options_nested_fields)).into());
404 }
405 transport_options_fields.push((7, tlv::TlvItemValueEnc::StructInvisible(container_options_nested_fields)).into());
406 }
407 if let Some(x) = transport_options.expiry_time { transport_options_fields.push((8, tlv::TlvItemValueEnc::UInt64(x)).into()); }
408 if let Some(listv) = transport_options.video_streams {
409 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
410 let mut nested_fields = Vec::new();
411 if let Some(x) = inner.video_stream_name { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
412 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
414 }).collect();
415 transport_options_fields.push((9, tlv::TlvItemValueEnc::Array(inner_vec)).into());
416 }
417 if let Some(listv) = transport_options.audio_streams {
418 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
419 let mut nested_fields = Vec::new();
420 if let Some(x) = inner.audio_stream_name { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
421 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
423 }).collect();
424 transport_options_fields.push((10, tlv::TlvItemValueEnc::Array(inner_vec)).into());
425 }
426 let tlv = tlv::TlvItemEnc {
427 tag: 0,
428 value: tlv::TlvItemValueEnc::StructInvisible(vec![
429 (0, tlv::TlvItemValueEnc::StructInvisible(transport_options_fields)).into(),
430 ]),
431 };
432 Ok(tlv.encode()?)
433}
434
435pub fn encode_deallocate_push_transport(connection_id: u8) -> anyhow::Result<Vec<u8>> {
437 let tlv = tlv::TlvItemEnc {
438 tag: 0,
439 value: tlv::TlvItemValueEnc::StructInvisible(vec![
440 (0, tlv::TlvItemValueEnc::UInt8(connection_id)).into(),
441 ]),
442 };
443 Ok(tlv.encode()?)
444}
445
446pub fn encode_modify_push_transport(connection_id: u8, transport_options: TransportOptions) -> anyhow::Result<Vec<u8>> {
448 let mut transport_options_fields = Vec::new();
450 if let Some(x) = transport_options.stream_usage { transport_options_fields.push((0, tlv::TlvItemValueEnc::UInt8(x)).into()); }
451 if let Some(x) = transport_options.url { transport_options_fields.push((4, tlv::TlvItemValueEnc::String(x.clone())).into()); }
455 if let Some(inner) = transport_options.trigger_options {
456 let mut trigger_options_nested_fields = Vec::new();
457 if let Some(x) = inner.trigger_type { trigger_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
458 if let Some(listv) = inner.motion_zones {
459 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
460 let mut nested_fields = Vec::new();
461 if let Some(x) = inner.sensitivity { nested_fields.push((1, tlv::TlvItemValueEnc::UInt8(x)).into()); }
463 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
464 }).collect();
465 trigger_options_nested_fields.push((1, tlv::TlvItemValueEnc::Array(inner_vec)).into());
466 }
467 if let Some(x) = inner.motion_sensitivity { trigger_options_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x)).into()); }
468 if let Some(inner) = inner.motion_time_control {
469 let mut motion_time_control_nested_fields = Vec::new();
470 if let Some(x) = inner.initial_duration { motion_time_control_nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
471 if let Some(x) = inner.augmentation_duration { motion_time_control_nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
472 if let Some(x) = inner.max_duration { motion_time_control_nested_fields.push((2, tlv::TlvItemValueEnc::UInt32(x)).into()); }
473 if let Some(x) = inner.blind_duration { motion_time_control_nested_fields.push((3, tlv::TlvItemValueEnc::UInt16(x)).into()); }
474 trigger_options_nested_fields.push((3, tlv::TlvItemValueEnc::StructInvisible(motion_time_control_nested_fields)).into());
475 }
476 if let Some(x) = inner.max_pre_roll_len { trigger_options_nested_fields.push((4, tlv::TlvItemValueEnc::UInt16(x)).into()); }
477 transport_options_fields.push((5, tlv::TlvItemValueEnc::StructInvisible(trigger_options_nested_fields)).into());
478 }
479 if let Some(x) = transport_options.ingest_method { transport_options_fields.push((6, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
480 if let Some(inner) = transport_options.container_options {
481 let mut container_options_nested_fields = Vec::new();
482 if let Some(x) = inner.container_type { container_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
483 if let Some(inner) = inner.cmaf_container_options {
484 let mut cmaf_container_options_nested_fields = Vec::new();
485 if let Some(x) = inner.cmaf_interface { cmaf_container_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
486 if let Some(x) = inner.segment_duration { cmaf_container_options_nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
487 if let Some(x) = inner.chunk_duration { cmaf_container_options_nested_fields.push((2, tlv::TlvItemValueEnc::UInt16(x)).into()); }
488 if let Some(x) = inner.session_group { cmaf_container_options_nested_fields.push((3, tlv::TlvItemValueEnc::UInt8(x)).into()); }
489 if let Some(x) = inner.track_name { cmaf_container_options_nested_fields.push((4, tlv::TlvItemValueEnc::String(x.clone())).into()); }
490 if let Some(x) = inner.cenc_key { cmaf_container_options_nested_fields.push((5, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
491 if let Some(x) = inner.cenc_key_id { cmaf_container_options_nested_fields.push((6, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
492 if let Some(x) = inner.metadata_enabled { cmaf_container_options_nested_fields.push((7, tlv::TlvItemValueEnc::Bool(x)).into()); }
493 container_options_nested_fields.push((1, tlv::TlvItemValueEnc::StructInvisible(cmaf_container_options_nested_fields)).into());
494 }
495 transport_options_fields.push((7, tlv::TlvItemValueEnc::StructInvisible(container_options_nested_fields)).into());
496 }
497 if let Some(x) = transport_options.expiry_time { transport_options_fields.push((8, tlv::TlvItemValueEnc::UInt64(x)).into()); }
498 if let Some(listv) = transport_options.video_streams {
499 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
500 let mut nested_fields = Vec::new();
501 if let Some(x) = inner.video_stream_name { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
502 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
504 }).collect();
505 transport_options_fields.push((9, tlv::TlvItemValueEnc::Array(inner_vec)).into());
506 }
507 if let Some(listv) = transport_options.audio_streams {
508 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
509 let mut nested_fields = Vec::new();
510 if let Some(x) = inner.audio_stream_name { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
511 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
513 }).collect();
514 transport_options_fields.push((10, tlv::TlvItemValueEnc::Array(inner_vec)).into());
515 }
516 let tlv = tlv::TlvItemEnc {
517 tag: 0,
518 value: tlv::TlvItemValueEnc::StructInvisible(vec![
519 (0, tlv::TlvItemValueEnc::UInt8(connection_id)).into(),
520 (1, tlv::TlvItemValueEnc::StructInvisible(transport_options_fields)).into(),
521 ]),
522 };
523 Ok(tlv.encode()?)
524}
525
526pub fn encode_set_transport_status(connection_id: Option<u8>, transport_status: TransportStatus) -> anyhow::Result<Vec<u8>> {
528 let tlv = tlv::TlvItemEnc {
529 tag: 0,
530 value: tlv::TlvItemValueEnc::StructInvisible(vec![
531 (0, tlv::TlvItemValueEnc::UInt8(connection_id.unwrap_or(0))).into(),
532 (1, tlv::TlvItemValueEnc::UInt8(transport_status.to_u8())).into(),
533 ]),
534 };
535 Ok(tlv.encode()?)
536}
537
538pub fn encode_manually_trigger_transport(connection_id: u8, activation_reason: TriggerActivationReason, time_control: TransportMotionTriggerTimeControl, user_defined: Vec<u8>) -> anyhow::Result<Vec<u8>> {
540 let mut time_control_fields = Vec::new();
542 if let Some(x) = time_control.initial_duration { time_control_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
543 if let Some(x) = time_control.augmentation_duration { time_control_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
544 if let Some(x) = time_control.max_duration { time_control_fields.push((2, tlv::TlvItemValueEnc::UInt32(x)).into()); }
545 if let Some(x) = time_control.blind_duration { time_control_fields.push((3, tlv::TlvItemValueEnc::UInt16(x)).into()); }
546 let tlv = tlv::TlvItemEnc {
547 tag: 0,
548 value: tlv::TlvItemValueEnc::StructInvisible(vec![
549 (0, tlv::TlvItemValueEnc::UInt8(connection_id)).into(),
550 (1, tlv::TlvItemValueEnc::UInt8(activation_reason.to_u8())).into(),
551 (2, tlv::TlvItemValueEnc::StructInvisible(time_control_fields)).into(),
552 (3, tlv::TlvItemValueEnc::OctetString(user_defined)).into(),
553 ]),
554 };
555 Ok(tlv.encode()?)
556}
557
558pub fn encode_find_transport(connection_id: Option<u8>) -> anyhow::Result<Vec<u8>> {
560 let tlv = tlv::TlvItemEnc {
561 tag: 0,
562 value: tlv::TlvItemValueEnc::StructInvisible(vec![
563 (0, tlv::TlvItemValueEnc::UInt8(connection_id.unwrap_or(0))).into(),
564 ]),
565 };
566 Ok(tlv.encode()?)
567}
568
569pub fn decode_supported_formats(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<SupportedFormat>> {
573 let mut res = Vec::new();
574 if let tlv::TlvItemValue::List(v) = inp {
575 for item in v {
576 res.push(SupportedFormat {
577 container_format: item.get_int(&[0]).and_then(|v| ContainerFormat::from_u8(v as u8)),
578 ingest_method: item.get_int(&[1]).and_then(|v| IngestMethods::from_u8(v as u8)),
579 });
580 }
581 }
582 Ok(res)
583}
584
585pub fn decode_current_connections(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TransportConfiguration>> {
587 let mut res = Vec::new();
588 if let tlv::TlvItemValue::List(v) = inp {
589 for item in v {
590 res.push(TransportConfiguration {
591 connection_id: item.get_int(&[0]).map(|v| v as u8),
592 transport_status: item.get_int(&[1]).and_then(|v| TransportStatus::from_u8(v as u8)),
593 transport_options: {
594 if let Some(nested_tlv) = item.get(&[2]) {
595 if let tlv::TlvItemValue::List(_) = nested_tlv {
596 let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
597 Some(TransportOptions {
598 stream_usage: nested_item.get_int(&[0]).map(|v| v as u8),
599 video_stream_id: nested_item.get_int(&[1]).map(|v| v as u8),
600 audio_stream_id: nested_item.get_int(&[2]).map(|v| v as u8),
601 tls_endpoint_id: nested_item.get_int(&[3]).map(|v| v as u8),
602 url: nested_item.get_string_owned(&[4]),
603 trigger_options: {
604 if let Some(nested_tlv) = nested_item.get(&[5]) {
605 if let tlv::TlvItemValue::List(_) = nested_tlv {
606 let nested_item = tlv::TlvItem { tag: 5, value: nested_tlv.clone() };
607 Some(TransportTriggerOptions {
608 trigger_type: nested_item.get_int(&[0]).and_then(|v| TransportTriggerType::from_u8(v as u8)),
609 motion_zones: {
610 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[1]) {
611 let mut items = Vec::new();
612 for list_item in l {
613 items.push(TransportZoneOptions {
614 zone: list_item.get_int(&[0]).map(|v| v as u8),
615 sensitivity: list_item.get_int(&[1]).map(|v| v as u8),
616 });
617 }
618 Some(items)
619 } else {
620 None
621 }
622 },
623 motion_sensitivity: nested_item.get_int(&[2]).map(|v| v as u8),
624 motion_time_control: {
625 if let Some(nested_tlv) = nested_item.get(&[3]) {
626 if let tlv::TlvItemValue::List(_) = nested_tlv {
627 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
628 Some(TransportMotionTriggerTimeControl {
629 initial_duration: nested_item.get_int(&[0]).map(|v| v as u16),
630 augmentation_duration: nested_item.get_int(&[1]).map(|v| v as u16),
631 max_duration: nested_item.get_int(&[2]).map(|v| v as u32),
632 blind_duration: nested_item.get_int(&[3]).map(|v| v as u16),
633 })
634 } else {
635 None
636 }
637 } else {
638 None
639 }
640 },
641 max_pre_roll_len: nested_item.get_int(&[4]).map(|v| v as u16),
642 })
643 } else {
644 None
645 }
646 } else {
647 None
648 }
649 },
650 ingest_method: nested_item.get_int(&[6]).and_then(|v| IngestMethods::from_u8(v as u8)),
651 container_options: {
652 if let Some(nested_tlv) = nested_item.get(&[7]) {
653 if let tlv::TlvItemValue::List(_) = nested_tlv {
654 let nested_item = tlv::TlvItem { tag: 7, value: nested_tlv.clone() };
655 Some(ContainerOptions {
656 container_type: nested_item.get_int(&[0]).and_then(|v| ContainerFormat::from_u8(v as u8)),
657 cmaf_container_options: {
658 if let Some(nested_tlv) = nested_item.get(&[1]) {
659 if let tlv::TlvItemValue::List(_) = nested_tlv {
660 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
661 Some(CMAFContainerOptions {
662 cmaf_interface: nested_item.get_int(&[0]).and_then(|v| CMAFInterface::from_u8(v as u8)),
663 segment_duration: nested_item.get_int(&[1]).map(|v| v as u16),
664 chunk_duration: nested_item.get_int(&[2]).map(|v| v as u16),
665 session_group: nested_item.get_int(&[3]).map(|v| v as u8),
666 track_name: nested_item.get_string_owned(&[4]),
667 cenc_key: nested_item.get_octet_string_owned(&[5]),
668 cenc_key_id: nested_item.get_octet_string_owned(&[6]),
669 metadata_enabled: nested_item.get_bool(&[7]),
670 })
671 } else {
672 None
673 }
674 } else {
675 None
676 }
677 },
678 })
679 } else {
680 None
681 }
682 } else {
683 None
684 }
685 },
686 expiry_time: nested_item.get_int(&[8]),
687 video_streams: {
688 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[9]) {
689 let mut items = Vec::new();
690 for list_item in l {
691 items.push(VideoStream {
692 video_stream_name: list_item.get_string_owned(&[0]),
693 video_stream_id: list_item.get_int(&[1]).map(|v| v as u8),
694 });
695 }
696 Some(items)
697 } else {
698 None
699 }
700 },
701 audio_streams: {
702 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[10]) {
703 let mut items = Vec::new();
704 for list_item in l {
705 items.push(AudioStream {
706 audio_stream_name: list_item.get_string_owned(&[0]),
707 audio_stream_id: list_item.get_int(&[1]).map(|v| v as u8),
708 });
709 }
710 Some(items)
711 } else {
712 None
713 }
714 },
715 })
716 } else {
717 None
718 }
719 } else {
720 None
721 }
722 },
723 });
724 }
725 }
726 Ok(res)
727}
728
729
730pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
742 if cluster_id != 0x0555 {
744 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0555, got {}\"}}", cluster_id);
745 }
746
747 match attribute_id {
748 0x0000 => {
749 match decode_supported_formats(tlv_value) {
750 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
751 Err(e) => format!("{{\"error\": \"{}\"}}", e),
752 }
753 }
754 0x0001 => {
755 match decode_current_connections(tlv_value) {
756 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
757 Err(e) => format!("{{\"error\": \"{}\"}}", e),
758 }
759 }
760 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
761 }
762}
763
764pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
769 vec![
770 (0x0000, "SupportedFormats"),
771 (0x0001, "CurrentConnections"),
772 ]
773}
774
775#[derive(Debug, serde::Serialize)]
776pub struct AllocatePushTransportResponse {
777 pub transport_configuration: Option<TransportConfiguration>,
778}
779
780#[derive(Debug, serde::Serialize)]
781pub struct FindTransportResponse {
782 pub transport_configurations: Option<Vec<TransportConfiguration>>,
783}
784
785pub fn decode_allocate_push_transport_response(inp: &tlv::TlvItemValue) -> anyhow::Result<AllocatePushTransportResponse> {
789 if let tlv::TlvItemValue::List(_fields) = inp {
790 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
791 Ok(AllocatePushTransportResponse {
792 transport_configuration: {
793 if let Some(nested_tlv) = item.get(&[0]) {
794 if let tlv::TlvItemValue::List(_) = nested_tlv {
795 let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
796 Some(TransportConfiguration {
797 connection_id: nested_item.get_int(&[0]).map(|v| v as u8),
798 transport_status: nested_item.get_int(&[1]).and_then(|v| TransportStatus::from_u8(v as u8)),
799 transport_options: {
800 if let Some(nested_tlv) = nested_item.get(&[2]) {
801 if let tlv::TlvItemValue::List(_) = nested_tlv {
802 let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
803 Some(TransportOptions {
804 stream_usage: nested_item.get_int(&[0]).map(|v| v as u8),
805 video_stream_id: nested_item.get_int(&[1]).map(|v| v as u8),
806 audio_stream_id: nested_item.get_int(&[2]).map(|v| v as u8),
807 tls_endpoint_id: nested_item.get_int(&[3]).map(|v| v as u8),
808 url: nested_item.get_string_owned(&[4]),
809 trigger_options: {
810 if let Some(nested_tlv) = nested_item.get(&[5]) {
811 if let tlv::TlvItemValue::List(_) = nested_tlv {
812 let nested_item = tlv::TlvItem { tag: 5, value: nested_tlv.clone() };
813 Some(TransportTriggerOptions {
814 trigger_type: nested_item.get_int(&[0]).and_then(|v| TransportTriggerType::from_u8(v as u8)),
815 motion_zones: {
816 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[1]) {
817 let mut items = Vec::new();
818 for list_item in l {
819 items.push(TransportZoneOptions {
820 zone: list_item.get_int(&[0]).map(|v| v as u8),
821 sensitivity: list_item.get_int(&[1]).map(|v| v as u8),
822 });
823 }
824 Some(items)
825 } else {
826 None
827 }
828 },
829 motion_sensitivity: nested_item.get_int(&[2]).map(|v| v as u8),
830 motion_time_control: {
831 if let Some(nested_tlv) = nested_item.get(&[3]) {
832 if let tlv::TlvItemValue::List(_) = nested_tlv {
833 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
834 Some(TransportMotionTriggerTimeControl {
835 initial_duration: nested_item.get_int(&[0]).map(|v| v as u16),
836 augmentation_duration: nested_item.get_int(&[1]).map(|v| v as u16),
837 max_duration: nested_item.get_int(&[2]).map(|v| v as u32),
838 blind_duration: nested_item.get_int(&[3]).map(|v| v as u16),
839 })
840 } else {
841 None
842 }
843 } else {
844 None
845 }
846 },
847 max_pre_roll_len: nested_item.get_int(&[4]).map(|v| v as u16),
848 })
849 } else {
850 None
851 }
852 } else {
853 None
854 }
855 },
856 ingest_method: nested_item.get_int(&[6]).and_then(|v| IngestMethods::from_u8(v as u8)),
857 container_options: {
858 if let Some(nested_tlv) = nested_item.get(&[7]) {
859 if let tlv::TlvItemValue::List(_) = nested_tlv {
860 let nested_item = tlv::TlvItem { tag: 7, value: nested_tlv.clone() };
861 Some(ContainerOptions {
862 container_type: nested_item.get_int(&[0]).and_then(|v| ContainerFormat::from_u8(v as u8)),
863 cmaf_container_options: {
864 if let Some(nested_tlv) = nested_item.get(&[1]) {
865 if let tlv::TlvItemValue::List(_) = nested_tlv {
866 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
867 Some(CMAFContainerOptions {
868 cmaf_interface: nested_item.get_int(&[0]).and_then(|v| CMAFInterface::from_u8(v as u8)),
869 segment_duration: nested_item.get_int(&[1]).map(|v| v as u16),
870 chunk_duration: nested_item.get_int(&[2]).map(|v| v as u16),
871 session_group: nested_item.get_int(&[3]).map(|v| v as u8),
872 track_name: nested_item.get_string_owned(&[4]),
873 cenc_key: nested_item.get_octet_string_owned(&[5]),
874 cenc_key_id: nested_item.get_octet_string_owned(&[6]),
875 metadata_enabled: nested_item.get_bool(&[7]),
876 })
877 } else {
878 None
879 }
880 } else {
881 None
882 }
883 },
884 })
885 } else {
886 None
887 }
888 } else {
889 None
890 }
891 },
892 expiry_time: nested_item.get_int(&[8]),
893 video_streams: {
894 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[9]) {
895 let mut items = Vec::new();
896 for list_item in l {
897 items.push(VideoStream {
898 video_stream_name: list_item.get_string_owned(&[0]),
899 video_stream_id: list_item.get_int(&[1]).map(|v| v as u8),
900 });
901 }
902 Some(items)
903 } else {
904 None
905 }
906 },
907 audio_streams: {
908 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[10]) {
909 let mut items = Vec::new();
910 for list_item in l {
911 items.push(AudioStream {
912 audio_stream_name: list_item.get_string_owned(&[0]),
913 audio_stream_id: list_item.get_int(&[1]).map(|v| v as u8),
914 });
915 }
916 Some(items)
917 } else {
918 None
919 }
920 },
921 })
922 } else {
923 None
924 }
925 } else {
926 None
927 }
928 },
929 })
930 } else {
931 None
932 }
933 } else {
934 None
935 }
936 },
937 })
938 } else {
939 Err(anyhow::anyhow!("Expected struct fields"))
940 }
941}
942
943pub fn decode_find_transport_response(inp: &tlv::TlvItemValue) -> anyhow::Result<FindTransportResponse> {
945 if let tlv::TlvItemValue::List(_fields) = inp {
946 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
947 Ok(FindTransportResponse {
948 transport_configurations: {
949 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
950 let mut items = Vec::new();
951 for list_item in l {
952 items.push(TransportConfiguration {
953 connection_id: list_item.get_int(&[0]).map(|v| v as u8),
954 transport_status: list_item.get_int(&[1]).and_then(|v| TransportStatus::from_u8(v as u8)),
955 transport_options: {
956 if let Some(nested_tlv) = list_item.get(&[2]) {
957 if let tlv::TlvItemValue::List(_) = nested_tlv {
958 let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
959 Some(TransportOptions {
960 stream_usage: nested_item.get_int(&[0]).map(|v| v as u8),
961 video_stream_id: nested_item.get_int(&[1]).map(|v| v as u8),
962 audio_stream_id: nested_item.get_int(&[2]).map(|v| v as u8),
963 tls_endpoint_id: nested_item.get_int(&[3]).map(|v| v as u8),
964 url: nested_item.get_string_owned(&[4]),
965 trigger_options: {
966 if let Some(nested_tlv) = nested_item.get(&[5]) {
967 if let tlv::TlvItemValue::List(_) = nested_tlv {
968 let nested_item = tlv::TlvItem { tag: 5, value: nested_tlv.clone() };
969 Some(TransportTriggerOptions {
970 trigger_type: nested_item.get_int(&[0]).and_then(|v| TransportTriggerType::from_u8(v as u8)),
971 motion_zones: {
972 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[1]) {
973 let mut items = Vec::new();
974 for list_item in l {
975 items.push(TransportZoneOptions {
976 zone: list_item.get_int(&[0]).map(|v| v as u8),
977 sensitivity: list_item.get_int(&[1]).map(|v| v as u8),
978 });
979 }
980 Some(items)
981 } else {
982 None
983 }
984 },
985 motion_sensitivity: nested_item.get_int(&[2]).map(|v| v as u8),
986 motion_time_control: {
987 if let Some(nested_tlv) = nested_item.get(&[3]) {
988 if let tlv::TlvItemValue::List(_) = nested_tlv {
989 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
990 Some(TransportMotionTriggerTimeControl {
991 initial_duration: nested_item.get_int(&[0]).map(|v| v as u16),
992 augmentation_duration: nested_item.get_int(&[1]).map(|v| v as u16),
993 max_duration: nested_item.get_int(&[2]).map(|v| v as u32),
994 blind_duration: nested_item.get_int(&[3]).map(|v| v as u16),
995 })
996 } else {
997 None
998 }
999 } else {
1000 None
1001 }
1002 },
1003 max_pre_roll_len: nested_item.get_int(&[4]).map(|v| v as u16),
1004 })
1005 } else {
1006 None
1007 }
1008 } else {
1009 None
1010 }
1011 },
1012 ingest_method: nested_item.get_int(&[6]).and_then(|v| IngestMethods::from_u8(v as u8)),
1013 container_options: {
1014 if let Some(nested_tlv) = nested_item.get(&[7]) {
1015 if let tlv::TlvItemValue::List(_) = nested_tlv {
1016 let nested_item = tlv::TlvItem { tag: 7, value: nested_tlv.clone() };
1017 Some(ContainerOptions {
1018 container_type: nested_item.get_int(&[0]).and_then(|v| ContainerFormat::from_u8(v as u8)),
1019 cmaf_container_options: {
1020 if let Some(nested_tlv) = nested_item.get(&[1]) {
1021 if let tlv::TlvItemValue::List(_) = nested_tlv {
1022 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
1023 Some(CMAFContainerOptions {
1024 cmaf_interface: nested_item.get_int(&[0]).and_then(|v| CMAFInterface::from_u8(v as u8)),
1025 segment_duration: nested_item.get_int(&[1]).map(|v| v as u16),
1026 chunk_duration: nested_item.get_int(&[2]).map(|v| v as u16),
1027 session_group: nested_item.get_int(&[3]).map(|v| v as u8),
1028 track_name: nested_item.get_string_owned(&[4]),
1029 cenc_key: nested_item.get_octet_string_owned(&[5]),
1030 cenc_key_id: nested_item.get_octet_string_owned(&[6]),
1031 metadata_enabled: nested_item.get_bool(&[7]),
1032 })
1033 } else {
1034 None
1035 }
1036 } else {
1037 None
1038 }
1039 },
1040 })
1041 } else {
1042 None
1043 }
1044 } else {
1045 None
1046 }
1047 },
1048 expiry_time: nested_item.get_int(&[8]),
1049 video_streams: {
1050 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[9]) {
1051 let mut items = Vec::new();
1052 for list_item in l {
1053 items.push(VideoStream {
1054 video_stream_name: list_item.get_string_owned(&[0]),
1055 video_stream_id: list_item.get_int(&[1]).map(|v| v as u8),
1056 });
1057 }
1058 Some(items)
1059 } else {
1060 None
1061 }
1062 },
1063 audio_streams: {
1064 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[10]) {
1065 let mut items = Vec::new();
1066 for list_item in l {
1067 items.push(AudioStream {
1068 audio_stream_name: list_item.get_string_owned(&[0]),
1069 audio_stream_id: list_item.get_int(&[1]).map(|v| v as u8),
1070 });
1071 }
1072 Some(items)
1073 } else {
1074 None
1075 }
1076 },
1077 })
1078 } else {
1079 None
1080 }
1081 } else {
1082 None
1083 }
1084 },
1085 });
1086 }
1087 Some(items)
1088 } else {
1089 None
1090 }
1091 },
1092 })
1093 } else {
1094 Err(anyhow::anyhow!("Expected struct fields"))
1095 }
1096}
1097
1098#[derive(Debug, serde::Serialize)]
1099pub struct PushTransportBeginEvent {
1100 pub connection_id: Option<u8>,
1101 pub trigger_type: Option<TransportTriggerType>,
1102 pub activation_reason: Option<TriggerActivationReason>,
1103 pub container_type: Option<ContainerFormat>,
1104 pub cmaf_session_number: Option<u64>,
1105 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
1106 pub vendor_specific_context: Option<Vec<u8>>,
1107}
1108
1109#[derive(Debug, serde::Serialize)]
1110pub struct PushTransportEndEvent {
1111 pub connection_id: Option<u8>,
1112 pub container_type: Option<ContainerFormat>,
1113 pub cmaf_session_number: Option<u64>,
1114}
1115
1116pub fn decode_push_transport_begin_event(inp: &tlv::TlvItemValue) -> anyhow::Result<PushTransportBeginEvent> {
1120 if let tlv::TlvItemValue::List(_fields) = inp {
1121 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1122 Ok(PushTransportBeginEvent {
1123 connection_id: item.get_int(&[0]).map(|v| v as u8),
1124 trigger_type: item.get_int(&[1]).and_then(|v| TransportTriggerType::from_u8(v as u8)),
1125 activation_reason: item.get_int(&[2]).and_then(|v| TriggerActivationReason::from_u8(v as u8)),
1126 container_type: item.get_int(&[3]).and_then(|v| ContainerFormat::from_u8(v as u8)),
1127 cmaf_session_number: item.get_int(&[4]),
1128 vendor_specific_context: item.get_octet_string_owned(&[5]),
1129 })
1130 } else {
1131 Err(anyhow::anyhow!("Expected struct fields"))
1132 }
1133}
1134
1135pub fn decode_push_transport_end_event(inp: &tlv::TlvItemValue) -> anyhow::Result<PushTransportEndEvent> {
1137 if let tlv::TlvItemValue::List(_fields) = inp {
1138 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1139 Ok(PushTransportEndEvent {
1140 connection_id: item.get_int(&[0]).map(|v| v as u8),
1141 container_type: item.get_int(&[1]).and_then(|v| ContainerFormat::from_u8(v as u8)),
1142 cmaf_session_number: item.get_int(&[2]),
1143 })
1144 } else {
1145 Err(anyhow::anyhow!("Expected struct fields"))
1146 }
1147}
1148