matc/clusters/codec/
push_av_stream_transport.rs

1//! Matter TLV encoders and decoders for Push AV Stream Transport Cluster
2//! Cluster ID: 0x0555
3//!
4//! This file is automatically generated from PushAVStreamTransport.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Import serialization helpers for octet strings
12use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14// Enum definitions
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum CMAFInterface {
19    /// CMAF Interface-1 Mode
20    Interface1 = 0,
21    /// CMAF Interface-2 Mode with DASH Support
22    Interface2dash = 1,
23    /// CMAF Interface-2 Mode with HLS Support
24    Interface2hls = 2,
25}
26
27impl CMAFInterface {
28    /// Convert from u8 value
29    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    /// Convert to u8 value
39    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 container format
54    Cmaf = 0,
55}
56
57impl ContainerFormat {
58    /// Convert from u8 value
59    pub fn from_u8(value: u8) -> Option<Self> {
60        match value {
61            0 => Some(ContainerFormat::Cmaf),
62            _ => None,
63        }
64    }
65
66    /// Convert to u8 value
67    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    /// CMAF ingestion format
82    Cmafingest = 0,
83}
84
85impl IngestMethods {
86    /// Convert from u8 value
87    pub fn from_u8(value: u8) -> Option<Self> {
88        match value {
89            0 => Some(IngestMethods::Cmafingest),
90            _ => None,
91        }
92    }
93
94    /// Convert to u8 value
95    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    /// The specified TLSEndpointID cannot be found.
110    Invalidtlsendpoint = 2,
111    /// The specified VideoStreamID or AudioStreamID cannot be found.
112    Invalidstream = 3,
113    /// The specified URL is invalid.
114    Invalidurl = 4,
115    /// A specified ZoneID was invalid.
116    Invalidzone = 5,
117    /// The specified combination of Ingestion method and Container format is not supported.
118    Invalidcombination = 6,
119    /// The trigger type is invalid for this command.
120    Invalidtriggertype = 7,
121    /// The Stream Transport Status is invalid for this command.
122    Invalidtransportstatus = 8,
123    /// The requested Container options are not supported with the streams indicated.
124    Invalidoptions = 9,
125    /// The requested StreamUsage is not allowed.
126    Invalidstreamusage = 10,
127    /// Time sync has not occurred yet.
128    Invalidtime = 11,
129    /// The requested pre roll length is not compatible with the streams key frame interval.
130    Invalidprerolllength = 12,
131    /// The requested Streams info contained duplicate entries.
132    Duplicatestreamvalues = 13,
133}
134
135impl StatusCode {
136    /// Convert from u8 value
137    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    /// Convert to u8 value
156    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    /// Push Transport can transport AV Streams
171    Active = 0,
172    /// Push Transport cannot transport AV Streams
173    Inactive = 1,
174}
175
176impl TransportStatus {
177    /// Convert from u8 value
178    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    /// Convert to u8 value
187    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    /// Triggered only via a command invocation
202    Command = 0,
203    /// Triggered via motion detection or command
204    Motion = 1,
205    /// Triggered always when transport status is Active
206    Continuous = 2,
207}
208
209impl TransportTriggerType {
210    /// Convert from u8 value
211    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    /// Convert to u8 value
221    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    /// Trigger has been activated by user action
236    Userinitiated = 0,
237    /// Trigger has been activated by automation
238    Automation = 1,
239    /// Trigger has been activated for emergency reasons
240    Emergency = 2,
241    /// Trigger has been activated by a doorbell press
242    Doorbellpressed = 3,
243}
244
245impl TriggerActivationReason {
246    /// Convert from u8 value
247    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    /// Convert to u8 value
258    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// Struct definitions
270
271#[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
354// Command encoders
355
356/// Encode AllocatePushTransport command (0x00)
357pub fn encode_allocate_push_transport(transport_options: TransportOptions) -> anyhow::Result<Vec<u8>> {
358            // Encode struct TransportOptionsStruct
359            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            // TODO: encoding for field video_stream_id (VideoStreamID) not implemented
362            // TODO: encoding for field audio_stream_id (AudioStreamID) not implemented
363            // TODO: encoding for field tls_endpoint_id (TLSEndpointID) not implemented
364            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                            // TODO: encoding for field zone (ZoneID) not implemented
372                            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                        // TODO: encoding for field video_stream_id (VideoStreamID) not implemented
413                    (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                        // TODO: encoding for field audio_stream_id (AudioStreamID) not implemented
422                    (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
435/// Encode DeallocatePushTransport command (0x02)
436pub 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
446/// Encode ModifyPushTransport command (0x03)
447pub fn encode_modify_push_transport(connection_id: u8, transport_options: TransportOptions) -> anyhow::Result<Vec<u8>> {
448            // Encode struct TransportOptionsStruct
449            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            // TODO: encoding for field video_stream_id (VideoStreamID) not implemented
452            // TODO: encoding for field audio_stream_id (AudioStreamID) not implemented
453            // TODO: encoding for field tls_endpoint_id (TLSEndpointID) not implemented
454            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                            // TODO: encoding for field zone (ZoneID) not implemented
462                            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                        // TODO: encoding for field video_stream_id (VideoStreamID) not implemented
503                    (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                        // TODO: encoding for field audio_stream_id (AudioStreamID) not implemented
512                    (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
526/// Encode SetTransportStatus command (0x04)
527pub 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
538/// Encode ManuallyTriggerTransport command (0x05)
539pub fn encode_manually_trigger_transport(connection_id: u8, activation_reason: TriggerActivationReason, time_control: TransportMotionTriggerTimeControl, user_defined: Vec<u8>) -> anyhow::Result<Vec<u8>> {
540            // Encode struct TransportMotionTriggerTimeControlStruct
541            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
558/// Encode FindTransport command (0x06)
559pub 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
569// Attribute decoders
570
571/// Decode SupportedFormats attribute (0x0000)
572pub 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
585/// Decode CurrentConnections attribute (0x0001)
586pub 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
730// JSON dispatcher function
731
732/// Decode attribute value and return as JSON string
733///
734/// # Parameters
735/// * `cluster_id` - The cluster identifier
736/// * `attribute_id` - The attribute identifier
737/// * `tlv_value` - The TLV value to decode
738///
739/// # Returns
740/// JSON string representation of the decoded value or error
741pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
742    // Verify this is the correct cluster
743    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
764/// Get list of all attributes supported by this cluster
765///
766/// # Returns
767/// Vector of tuples containing (attribute_id, attribute_name)
768pub 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
785// Command response decoders
786
787/// Decode AllocatePushTransportResponse command response (01)
788pub 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
943/// Decode FindTransportResponse command response (07)
944pub 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
1116// Event decoders
1117
1118/// Decode PushTransportBegin event (0x00, priority: info)
1119pub 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
1135/// Decode PushTransportEnd event (0x01, priority: info)
1136pub 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