Files
airdisplay-desktop/src-tauri/src/adp/frame.rs

89 lines
2.6 KiB
Rust

//! ADP frame protocol definition
//!
//! Frame format (8-byte header):
//! ```text
//! 0 1 2 3
//! 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | Magic (0xAD) | Version | Type | Flags |
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | Sequence Number | Payload Length |
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! ```
//!
//! Types:
//! - 0x01: Capability Request/Response (handshake)
//! - 0x02: Video Frame (H.264 NAL unit)
//! - 0x03: Keepalive
//! - 0x04: UIBC Event (touch/keyboard from phone to PC)
//! - 0x05: Resolution Change Notification
pub const ADP_MAGIC: u16 = 0x0BAD;
pub const ADP_VERSION: u8 = 1;
pub const ADP_DEFAULT_PORT: u16 = 7935;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameType {
Capability = 0x01,
VideoFrame = 0x02,
Keepalive = 0x03,
UibcEvent = 0x04,
ResolutionChange = 0x05,
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct FrameHeader {
pub magic: u16, // 0x0BAD
pub version: u8, // 1
pub frame_type: u8, // FrameType
pub flags: u8, // reserved
pub sequence: u32, // sequence number
pub payload_len: u32, // payload length
}
impl FrameHeader {
pub const SIZE: usize = 12;
pub fn new(frame_type: FrameType, sequence: u32, payload_len: u32) -> Self {
Self {
magic: ADP_MAGIC,
version: ADP_VERSION,
frame_type: frame_type as u8,
flags: 0,
sequence,
payload_len,
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(Self::SIZE);
buf.extend_from_slice(&self.magic.to_be_bytes());
buf.push(self.version);
buf.push(self.frame_type);
buf.push(self.flags);
buf.extend_from_slice(&self.sequence.to_be_bytes());
buf.extend_from_slice(&self.payload_len.to_be_bytes());
buf
}
pub fn from_bytes(data: &[u8]) -> Option<Self> {
if data.len() < Self::SIZE {
return None;
}
let magic = u16::from_be_bytes([data[0], data[1]]);
if magic != ADP_MAGIC {
return None;
}
Some(Self {
magic,
version: data[2],
frame_type: data[3],
flags: data[4],
sequence: u32::from_be_bytes([data[5], data[6], data[7], data[8]]),
payload_len: u32::from_be_bytes([data[9], data[10], data[11], data[12]]),
})
}
}