use crate::tlv;
use anyhow;
use serde_json;
use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum AudioCodec {
Opus = 0,
AacLc = 1,
}
impl AudioCodec {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(AudioCodec::Opus),
1 => Some(AudioCodec::AacLc),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<AudioCodec> for u8 {
fn from(val: AudioCodec) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ImageCodec {
Jpeg = 0,
Heic = 1,
}
impl ImageCodec {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ImageCodec::Jpeg),
1 => Some(ImageCodec::Heic),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ImageCodec> for u8 {
fn from(val: ImageCodec) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum TriStateAuto {
Off = 0,
On = 1,
Auto = 2,
}
impl TriStateAuto {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(TriStateAuto::Off),
1 => Some(TriStateAuto::On),
2 => Some(TriStateAuto::Auto),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<TriStateAuto> for u8 {
fn from(val: TriStateAuto) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum TwoWayTalkSupportType {
Notsupported = 0,
Halfduplex = 1,
Fullduplex = 2,
}
impl TwoWayTalkSupportType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(TwoWayTalkSupportType::Notsupported),
1 => Some(TwoWayTalkSupportType::Halfduplex),
2 => Some(TwoWayTalkSupportType::Fullduplex),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<TwoWayTalkSupportType> for u8 {
fn from(val: TwoWayTalkSupportType) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum VideoCodec {
H264 = 0,
Hevc = 1,
Vvc = 2,
Av1 = 3,
}
impl VideoCodec {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(VideoCodec::H264),
1 => Some(VideoCodec::Hevc),
2 => Some(VideoCodec::Vvc),
3 => Some(VideoCodec::Av1),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<VideoCodec> for u8 {
fn from(val: VideoCodec) -> Self {
val as u8
}
}
#[derive(Debug, serde::Serialize)]
pub struct AVMetadata {
pub utc_time: Option<u64>,
pub motion_zones_active: Option<Vec<u8>>,
pub black_and_white_active: Option<bool>,
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub user_defined: Option<Vec<u8>>,
}
#[derive(Debug, serde::Serialize)]
pub struct AudioCapabilities {
pub max_number_of_channels: Option<u8>,
pub supported_codecs: Option<Vec<AudioCodec>>,
pub supported_sample_rates: Option<Vec<u32>>,
pub supported_bit_depths: Option<Vec<u8>>,
}
#[derive(Debug, serde::Serialize)]
pub struct AudioStream {
pub audio_stream_id: Option<u8>,
pub stream_usage: Option<u8>,
pub audio_codec: Option<AudioCodec>,
pub channel_count: Option<u8>,
pub sample_rate: Option<u32>,
pub bit_rate: Option<u32>,
pub bit_depth: Option<u8>,
pub reference_count: Option<u8>,
}
#[derive(Debug, serde::Serialize)]
pub struct RateDistortionTradeOffPoints {
pub codec: Option<VideoCodec>,
pub resolution: Option<VideoResolution>,
pub min_bit_rate: Option<u32>,
}
#[derive(Debug, serde::Serialize)]
pub struct SnapshotCapabilities {
pub resolution: Option<VideoResolution>,
pub max_frame_rate: Option<u16>,
pub image_codec: Option<ImageCodec>,
pub requires_encoded_pixels: Option<bool>,
pub requires_hardware_encoder: Option<bool>,
}
#[derive(Debug, serde::Serialize)]
pub struct SnapshotStream {
pub snapshot_stream_id: Option<u8>,
pub image_codec: Option<ImageCodec>,
pub frame_rate: Option<u16>,
pub min_resolution: Option<VideoResolution>,
pub max_resolution: Option<VideoResolution>,
pub quality: Option<u8>,
pub reference_count: Option<u8>,
pub encoded_pixels: Option<bool>,
pub hardware_encoder: Option<bool>,
pub watermark_enabled: Option<bool>,
pub osd_enabled: Option<bool>,
}
#[derive(Debug, serde::Serialize)]
pub struct VideoResolution {
pub width: Option<u16>,
pub height: Option<u16>,
}
#[derive(Debug, serde::Serialize)]
pub struct VideoSensorParams {
pub sensor_width: Option<u16>,
pub sensor_height: Option<u16>,
pub max_fps: Option<u16>,
pub max_hdrfps: Option<u16>,
}
#[derive(Debug, serde::Serialize)]
pub struct VideoStream {
pub video_stream_id: Option<u8>,
pub stream_usage: Option<u8>,
pub video_codec: Option<VideoCodec>,
pub min_frame_rate: Option<u16>,
pub max_frame_rate: Option<u16>,
pub min_resolution: Option<VideoResolution>,
pub max_resolution: Option<VideoResolution>,
pub min_bit_rate: Option<u32>,
pub max_bit_rate: Option<u32>,
pub key_frame_interval: Option<u16>,
pub watermark_enabled: Option<bool>,
pub osd_enabled: Option<bool>,
pub reference_count: Option<u8>,
}
pub fn encode_audio_stream_allocate(stream_usage: u8, audio_codec: AudioCodec, channel_count: u8, sample_rate: u32, bit_rate: u32, bit_depth: u8) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(stream_usage)).into(),
(1, tlv::TlvItemValueEnc::UInt8(audio_codec.to_u8())).into(),
(2, tlv::TlvItemValueEnc::UInt8(channel_count)).into(),
(3, tlv::TlvItemValueEnc::UInt32(sample_rate)).into(),
(4, tlv::TlvItemValueEnc::UInt32(bit_rate)).into(),
(5, tlv::TlvItemValueEnc::UInt8(bit_depth)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_audio_stream_deallocate(audio_stream_id: u8) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(audio_stream_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub struct VideoStreamAllocateParams {
pub stream_usage: u8,
pub video_codec: VideoCodec,
pub min_frame_rate: u16,
pub max_frame_rate: u16,
pub min_resolution: VideoResolution,
pub max_resolution: VideoResolution,
pub min_bit_rate: u32,
pub max_bit_rate: u32,
pub key_frame_interval: u16,
pub watermark_enabled: bool,
pub osd_enabled: bool,
}
pub fn encode_video_stream_allocate(params: VideoStreamAllocateParams) -> anyhow::Result<Vec<u8>> {
let mut min_resolution_fields = Vec::new();
if let Some(x) = params.min_resolution.width { min_resolution_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = params.min_resolution.height { min_resolution_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
let mut max_resolution_fields = Vec::new();
if let Some(x) = params.max_resolution.width { max_resolution_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = params.max_resolution.height { max_resolution_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(params.stream_usage)).into(),
(1, tlv::TlvItemValueEnc::UInt8(params.video_codec.to_u8())).into(),
(2, tlv::TlvItemValueEnc::UInt16(params.min_frame_rate)).into(),
(3, tlv::TlvItemValueEnc::UInt16(params.max_frame_rate)).into(),
(4, tlv::TlvItemValueEnc::StructInvisible(min_resolution_fields)).into(),
(5, tlv::TlvItemValueEnc::StructInvisible(max_resolution_fields)).into(),
(6, tlv::TlvItemValueEnc::UInt32(params.min_bit_rate)).into(),
(7, tlv::TlvItemValueEnc::UInt32(params.max_bit_rate)).into(),
(8, tlv::TlvItemValueEnc::UInt16(params.key_frame_interval)).into(),
(9, tlv::TlvItemValueEnc::Bool(params.watermark_enabled)).into(),
(10, tlv::TlvItemValueEnc::Bool(params.osd_enabled)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_video_stream_modify(video_stream_id: u8, watermark_enabled: bool, osd_enabled: bool) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(video_stream_id)).into(),
(1, tlv::TlvItemValueEnc::Bool(watermark_enabled)).into(),
(2, tlv::TlvItemValueEnc::Bool(osd_enabled)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_video_stream_deallocate(video_stream_id: u8) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(video_stream_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_snapshot_stream_allocate(image_codec: ImageCodec, max_frame_rate: u16, min_resolution: VideoResolution, max_resolution: VideoResolution, quality: u8, watermark_enabled: bool, osd_enabled: bool) -> anyhow::Result<Vec<u8>> {
let mut min_resolution_fields = Vec::new();
if let Some(x) = min_resolution.width { min_resolution_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = min_resolution.height { min_resolution_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
let mut max_resolution_fields = Vec::new();
if let Some(x) = max_resolution.width { max_resolution_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = max_resolution.height { max_resolution_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(image_codec.to_u8())).into(),
(1, tlv::TlvItemValueEnc::UInt16(max_frame_rate)).into(),
(2, tlv::TlvItemValueEnc::StructInvisible(min_resolution_fields)).into(),
(3, tlv::TlvItemValueEnc::StructInvisible(max_resolution_fields)).into(),
(4, tlv::TlvItemValueEnc::UInt8(quality)).into(),
(5, tlv::TlvItemValueEnc::Bool(watermark_enabled)).into(),
(6, tlv::TlvItemValueEnc::Bool(osd_enabled)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_snapshot_stream_modify(snapshot_stream_id: u8, watermark_enabled: bool, osd_enabled: bool) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(snapshot_stream_id)).into(),
(1, tlv::TlvItemValueEnc::Bool(watermark_enabled)).into(),
(2, tlv::TlvItemValueEnc::Bool(osd_enabled)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_snapshot_stream_deallocate(snapshot_stream_id: u8) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(snapshot_stream_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_set_stream_priorities(stream_priorities: Vec<u8>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::StructAnon(stream_priorities.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_capture_snapshot(snapshot_stream_id: Option<u8>, requested_resolution: VideoResolution) -> anyhow::Result<Vec<u8>> {
let mut requested_resolution_fields = Vec::new();
if let Some(x) = requested_resolution.width { requested_resolution_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = requested_resolution.height { requested_resolution_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(snapshot_stream_id.unwrap_or(0))).into(),
(1, tlv::TlvItemValueEnc::StructInvisible(requested_resolution_fields)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn decode_max_concurrent_encoders(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_max_encoded_pixel_rate(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u32)
} else {
Err(anyhow::anyhow!("Expected UInt32"))
}
}
pub fn decode_video_sensor_params(inp: &tlv::TlvItemValue) -> anyhow::Result<VideoSensorParams> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(VideoSensorParams {
sensor_width: item.get_int(&[0]).map(|v| v as u16),
sensor_height: item.get_int(&[1]).map(|v| v as u16),
max_fps: item.get_int(&[2]).map(|v| v as u16),
max_hdrfps: item.get_int(&[3]).map(|v| v as u16),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_night_vision_uses_infrared(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_min_viewport_resolution(inp: &tlv::TlvItemValue) -> anyhow::Result<VideoResolution> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(VideoResolution {
width: item.get_int(&[0]).map(|v| v as u16),
height: item.get_int(&[1]).map(|v| v as u16),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_rate_distortion_trade_off_points(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<RateDistortionTradeOffPoints>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(RateDistortionTradeOffPoints {
codec: item.get_int(&[0]).and_then(|v| VideoCodec::from_u8(v as u8)),
resolution: {
if let Some(nested_tlv) = item.get(&[1]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
Some(VideoResolution {
width: nested_item.get_int(&[0]).map(|v| v as u16),
height: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
min_bit_rate: item.get_int(&[2]).map(|v| v as u32),
});
}
}
Ok(res)
}
pub fn decode_max_content_buffer_size(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u32)
} else {
Err(anyhow::anyhow!("Expected UInt32"))
}
}
pub fn decode_microphone_capabilities(inp: &tlv::TlvItemValue) -> anyhow::Result<AudioCapabilities> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(AudioCapabilities {
max_number_of_channels: item.get_int(&[0]).map(|v| v as u8),
supported_codecs: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
let items: Vec<AudioCodec> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { AudioCodec::from_u8(*v as u8) } else { None } }).collect();
Some(items)
} else {
None
}
},
supported_sample_rates: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
Some(items)
} else {
None
}
},
supported_bit_depths: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
Some(items)
} else {
None
}
},
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_speaker_capabilities(inp: &tlv::TlvItemValue) -> anyhow::Result<AudioCapabilities> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(AudioCapabilities {
max_number_of_channels: item.get_int(&[0]).map(|v| v as u8),
supported_codecs: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
let items: Vec<AudioCodec> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { AudioCodec::from_u8(*v as u8) } else { None } }).collect();
Some(items)
} else {
None
}
},
supported_sample_rates: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
Some(items)
} else {
None
}
},
supported_bit_depths: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
Some(items)
} else {
None
}
},
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_two_way_talk_support(inp: &tlv::TlvItemValue) -> anyhow::Result<TwoWayTalkSupportType> {
if let tlv::TlvItemValue::Int(v) = inp {
TwoWayTalkSupportType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_snapshot_capabilities(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<SnapshotCapabilities>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(SnapshotCapabilities {
resolution: {
if let Some(nested_tlv) = item.get(&[0]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
Some(VideoResolution {
width: nested_item.get_int(&[0]).map(|v| v as u16),
height: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
max_frame_rate: item.get_int(&[1]).map(|v| v as u16),
image_codec: item.get_int(&[2]).and_then(|v| ImageCodec::from_u8(v as u8)),
requires_encoded_pixels: item.get_bool(&[3]),
requires_hardware_encoder: item.get_bool(&[4]),
});
}
}
Ok(res)
}
pub fn decode_max_network_bandwidth(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u32)
} else {
Err(anyhow::anyhow!("Expected UInt32"))
}
}
pub fn decode_current_frame_rate(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u16)
} else {
Err(anyhow::anyhow!("Expected UInt16"))
}
}
pub fn decode_hdr_mode_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_supported_stream_usages(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u8>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
if let tlv::TlvItemValue::Int(i) = &item.value {
res.push(*i as u8);
}
}
}
Ok(res)
}
pub fn decode_allocated_video_streams(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<VideoStream>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(VideoStream {
video_stream_id: item.get_int(&[0]).map(|v| v as u8),
stream_usage: item.get_int(&[1]).map(|v| v as u8),
video_codec: item.get_int(&[2]).and_then(|v| VideoCodec::from_u8(v as u8)),
min_frame_rate: item.get_int(&[3]).map(|v| v as u16),
max_frame_rate: item.get_int(&[4]).map(|v| v as u16),
min_resolution: {
if let Some(nested_tlv) = item.get(&[5]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 5, value: nested_tlv.clone() };
Some(VideoResolution {
width: nested_item.get_int(&[0]).map(|v| v as u16),
height: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
max_resolution: {
if let Some(nested_tlv) = item.get(&[6]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 6, value: nested_tlv.clone() };
Some(VideoResolution {
width: nested_item.get_int(&[0]).map(|v| v as u16),
height: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
min_bit_rate: item.get_int(&[7]).map(|v| v as u32),
max_bit_rate: item.get_int(&[8]).map(|v| v as u32),
key_frame_interval: item.get_int(&[9]).map(|v| v as u16),
watermark_enabled: item.get_bool(&[10]),
osd_enabled: item.get_bool(&[11]),
reference_count: item.get_int(&[12]).map(|v| v as u8),
});
}
}
Ok(res)
}
pub fn decode_allocated_audio_streams(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AudioStream>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(AudioStream {
audio_stream_id: item.get_int(&[0]).map(|v| v as u8),
stream_usage: item.get_int(&[1]).map(|v| v as u8),
audio_codec: item.get_int(&[2]).and_then(|v| AudioCodec::from_u8(v as u8)),
channel_count: item.get_int(&[3]).map(|v| v as u8),
sample_rate: item.get_int(&[4]).map(|v| v as u32),
bit_rate: item.get_int(&[5]).map(|v| v as u32),
bit_depth: item.get_int(&[6]).map(|v| v as u8),
reference_count: item.get_int(&[7]).map(|v| v as u8),
});
}
}
Ok(res)
}
pub fn decode_allocated_snapshot_streams(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<SnapshotStream>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(SnapshotStream {
snapshot_stream_id: item.get_int(&[0]).map(|v| v as u8),
image_codec: item.get_int(&[1]).and_then(|v| ImageCodec::from_u8(v as u8)),
frame_rate: item.get_int(&[2]).map(|v| v as u16),
min_resolution: {
if let Some(nested_tlv) = item.get(&[3]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
Some(VideoResolution {
width: nested_item.get_int(&[0]).map(|v| v as u16),
height: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
max_resolution: {
if let Some(nested_tlv) = item.get(&[4]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
Some(VideoResolution {
width: nested_item.get_int(&[0]).map(|v| v as u16),
height: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
quality: item.get_int(&[5]).map(|v| v as u8),
reference_count: item.get_int(&[6]).map(|v| v as u8),
encoded_pixels: item.get_bool(&[7]),
hardware_encoder: item.get_bool(&[8]),
watermark_enabled: item.get_bool(&[9]),
osd_enabled: item.get_bool(&[10]),
});
}
}
Ok(res)
}
pub fn decode_stream_usage_priorities(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u8>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
if let tlv::TlvItemValue::Int(i) = &item.value {
res.push(*i as u8);
}
}
}
Ok(res)
}
pub fn decode_soft_recording_privacy_mode_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_soft_livestream_privacy_mode_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_hard_privacy_mode_on(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_night_vision(inp: &tlv::TlvItemValue) -> anyhow::Result<TriStateAuto> {
if let tlv::TlvItemValue::Int(v) = inp {
TriStateAuto::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_night_vision_illum(inp: &tlv::TlvItemValue) -> anyhow::Result<TriStateAuto> {
if let tlv::TlvItemValue::Int(v) = inp {
TriStateAuto::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_viewport(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_speaker_muted(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_speaker_volume_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_speaker_max_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_speaker_min_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_microphone_muted(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_microphone_volume_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_microphone_max_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_microphone_min_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_microphone_agc_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_image_rotation(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u16)
} else {
Err(anyhow::anyhow!("Expected UInt16"))
}
}
pub fn decode_image_flip_horizontal(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_image_flip_vertical(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_local_video_recording_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_local_snapshot_recording_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_status_light_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_status_light_brightness(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
if cluster_id != 0x0551 {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0551, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_max_concurrent_encoders(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_max_encoded_pixel_rate(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0002 => {
match decode_video_sensor_params(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0003 => {
match decode_night_vision_uses_infrared(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0004 => {
match decode_min_viewport_resolution(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0005 => {
match decode_rate_distortion_trade_off_points(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0006 => {
match decode_max_content_buffer_size(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0007 => {
match decode_microphone_capabilities(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0008 => {
match decode_speaker_capabilities(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0009 => {
match decode_two_way_talk_support(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000A => {
match decode_snapshot_capabilities(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000B => {
match decode_max_network_bandwidth(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000C => {
match decode_current_frame_rate(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000D => {
match decode_hdr_mode_enabled(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000E => {
match decode_supported_stream_usages(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000F => {
match decode_allocated_video_streams(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0010 => {
match decode_allocated_audio_streams(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0011 => {
match decode_allocated_snapshot_streams(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0012 => {
match decode_stream_usage_priorities(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0013 => {
match decode_soft_recording_privacy_mode_enabled(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0014 => {
match decode_soft_livestream_privacy_mode_enabled(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0015 => {
match decode_hard_privacy_mode_on(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0016 => {
match decode_night_vision(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0017 => {
match decode_night_vision_illum(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0018 => {
match decode_viewport(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0019 => {
match decode_speaker_muted(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x001A => {
match decode_speaker_volume_level(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x001B => {
match decode_speaker_max_level(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x001C => {
match decode_speaker_min_level(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x001D => {
match decode_microphone_muted(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x001E => {
match decode_microphone_volume_level(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x001F => {
match decode_microphone_max_level(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0020 => {
match decode_microphone_min_level(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0021 => {
match decode_microphone_agc_enabled(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0022 => {
match decode_image_rotation(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0023 => {
match decode_image_flip_horizontal(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0024 => {
match decode_image_flip_vertical(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0025 => {
match decode_local_video_recording_enabled(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0026 => {
match decode_local_snapshot_recording_enabled(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0027 => {
match decode_status_light_enabled(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0028 => {
match decode_status_light_brightness(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
_ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
}
}
pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
vec![
(0x0000, "MaxConcurrentEncoders"),
(0x0001, "MaxEncodedPixelRate"),
(0x0002, "VideoSensorParams"),
(0x0003, "NightVisionUsesInfrared"),
(0x0004, "MinViewportResolution"),
(0x0005, "RateDistortionTradeOffPoints"),
(0x0006, "MaxContentBufferSize"),
(0x0007, "MicrophoneCapabilities"),
(0x0008, "SpeakerCapabilities"),
(0x0009, "TwoWayTalkSupport"),
(0x000A, "SnapshotCapabilities"),
(0x000B, "MaxNetworkBandwidth"),
(0x000C, "CurrentFrameRate"),
(0x000D, "HDRModeEnabled"),
(0x000E, "SupportedStreamUsages"),
(0x000F, "AllocatedVideoStreams"),
(0x0010, "AllocatedAudioStreams"),
(0x0011, "AllocatedSnapshotStreams"),
(0x0012, "StreamUsagePriorities"),
(0x0013, "SoftRecordingPrivacyModeEnabled"),
(0x0014, "SoftLivestreamPrivacyModeEnabled"),
(0x0015, "HardPrivacyModeOn"),
(0x0016, "NightVision"),
(0x0017, "NightVisionIllum"),
(0x0018, "Viewport"),
(0x0019, "SpeakerMuted"),
(0x001A, "SpeakerVolumeLevel"),
(0x001B, "SpeakerMaxLevel"),
(0x001C, "SpeakerMinLevel"),
(0x001D, "MicrophoneMuted"),
(0x001E, "MicrophoneVolumeLevel"),
(0x001F, "MicrophoneMaxLevel"),
(0x0020, "MicrophoneMinLevel"),
(0x0021, "MicrophoneAGCEnabled"),
(0x0022, "ImageRotation"),
(0x0023, "ImageFlipHorizontal"),
(0x0024, "ImageFlipVertical"),
(0x0025, "LocalVideoRecordingEnabled"),
(0x0026, "LocalSnapshotRecordingEnabled"),
(0x0027, "StatusLightEnabled"),
(0x0028, "StatusLightBrightness"),
]
}
#[derive(Debug, serde::Serialize)]
pub struct AudioStreamAllocateResponse {
pub audio_stream_id: Option<u8>,
}
#[derive(Debug, serde::Serialize)]
pub struct VideoStreamAllocateResponse {
pub video_stream_id: Option<u8>,
}
#[derive(Debug, serde::Serialize)]
pub struct SnapshotStreamAllocateResponse {
pub snapshot_stream_id: Option<u8>,
}
#[derive(Debug, serde::Serialize)]
pub struct CaptureSnapshotResponse {
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub data: Option<Vec<u8>>,
pub image_codec: Option<ImageCodec>,
pub resolution: Option<VideoResolution>,
}
pub fn decode_audio_stream_allocate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<AudioStreamAllocateResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(AudioStreamAllocateResponse {
audio_stream_id: item.get_int(&[0]).map(|v| v as u8),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_video_stream_allocate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<VideoStreamAllocateResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(VideoStreamAllocateResponse {
video_stream_id: item.get_int(&[0]).map(|v| v as u8),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_snapshot_stream_allocate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SnapshotStreamAllocateResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(SnapshotStreamAllocateResponse {
snapshot_stream_id: item.get_int(&[0]).map(|v| v as u8),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_capture_snapshot_response(inp: &tlv::TlvItemValue) -> anyhow::Result<CaptureSnapshotResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(CaptureSnapshotResponse {
data: item.get_octet_string_owned(&[0]),
image_codec: item.get_int(&[1]).and_then(|v| ImageCodec::from_u8(v as u8)),
resolution: {
if let Some(nested_tlv) = item.get(&[2]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
Some(VideoResolution {
width: nested_item.get_int(&[0]).map(|v| v as u16),
height: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}