use std::net::Ipv4Addr;
use std::time::Duration;
use crate::event::Event;
use crate::state::{SdpPages, State};
use crate::types::{V2ipSinkSdp, V2IP_SDP_MAX};
use crate::wire::{
cstr, V2ipSdpClockMode, V2ipSdpClockState, V2ipSdpEncoding, V2ipSdpFlag, V2ipSdpMediaClock,
V2ipSdpRefClock, UID_LEN,
};
use super::handlers::{u16_at, u32_at};
use super::Rx;
const SDP_HEADER_SIZE: usize = UID_LEN + 4;
const SDP_STATUS: u8 = 0;
const SDP_DESCRIPTION: u8 = 5;
const SDP_PAGE_HEADER_SIZE: usize = 8;
const SDP_PAGE_TIMEOUT: Duration = Duration::from_secs(5);
const SDP_STATUS_SIZE: usize = 80;
pub(super) fn sink_sdp(state: &mut State, rx: &Rx<'_>, ev: &mut Vec<Event>) {
let p = rx.frame.payload();
if p.len() < SDP_HEADER_SIZE {
return;
}
if p[UID_LEN] == SDP_DESCRIPTION {
description_page(state, rx, ev);
return;
}
if p[UID_LEN] != SDP_STATUS {
return;
}
let mut s = p[SDP_HEADER_SIZE..].to_vec();
s.resize(s.len().max(SDP_STATUS_SIZE), 0);
let status = V2ipSinkSdp {
flags: V2ipSdpFlag::from_bits(s[0]),
screen: s[1],
encoding: V2ipSdpEncoding::from_wire(s[2]),
channels: s[3],
payload_type: s[4],
media_clock: V2ipSdpMediaClock::from_wire(s[5]),
ref_clock: V2ipSdpRefClock::from_wire(s[6]),
clock_mode: V2ipSdpClockMode::from_wire(s[7]),
clock_state: V2ipSdpClockState::from_wire(s[8]),
port: u16_at(&s, 10),
rate_hz: u32_at(&s, 12),
media_clock_offset: u32_at(&s, 16),
group: Ipv4Addr::new(s[20], s[21], s[22], s[23]),
name: cstr(&s[24..71]),
layout: cstr(&s[72..79]),
};
if let Some(device) = state.device_mut(rx.sender()) {
if !status.is_active() {
device.v2ip_sink_description_pages = None;
device.set_v2ip_sink_description(None, ev);
}
device.set_v2ip_sink_sdp(status, ev);
}
}
fn description_page(state: &mut State, rx: &Rx<'_>, ev: &mut Vec<Event>) {
let p = rx.frame.payload();
if rx.frame.uid(0) != Some(rx.sender()) {
return;
}
let Some(page) = p.get(SDP_HEADER_SIZE..SDP_HEADER_SIZE + SDP_PAGE_HEADER_SIZE) else {
return;
};
let (total, offset, len) = (u16_at(page, 0), u16_at(page, 2), u16_at(page, 4));
let start = SDP_HEADER_SIZE + SDP_PAGE_HEADER_SIZE;
let Some(text) = p.get(start..start + usize::from(len)) else {
return;
};
let Some(device) = state.device_mut(rx.sender()) else {
return;
};
let mut pages = match device.v2ip_sink_description_pages.take() {
_ if offset == 0 => {
if total == 0 || usize::from(total) > V2IP_SDP_MAX {
return;
}
SdpPages {
total,
data: Vec::with_capacity(usize::from(total)),
at: rx.timestamp,
}
}
Some(pages)
if pages.total == total
&& pages.data.len() == usize::from(offset)
&& rx.timestamp.saturating_duration_since(pages.at) <= SDP_PAGE_TIMEOUT =>
{
pages
}
_ => return,
};
if pages.data.len() + text.len() > usize::from(total) {
return;
}
pages.data.extend_from_slice(text);
pages.at = rx.timestamp;
if pages.data.len() < usize::from(total) {
device.v2ip_sink_description_pages = Some(pages);
return;
}
let description = String::from_utf8_lossy(&pages.data).into_owned();
device.set_v2ip_sink_description(Some(description), ev);
}