use crate::consts::frametype;
use crate::ie::{self, Ie};
const FULL_FRAME_BIT: u16 = 0x8000;
const RETRANSMIT_BIT: u16 = 0x8000;
const CALL_MASK: u16 = 0x7FFF;
const SUBCLASS_C_BIT: u8 = 0x80;
fn is_media(frametype: u8) -> bool {
matches!(
frametype,
frametype::DTMF
| frametype::VOICE
| frametype::VIDEO
| frametype::TEXT
| frametype::IMAGE
| frametype::HTML
| frametype::CNG
)
}
#[derive(Debug, Clone)]
pub struct FullFrame {
pub src_call: u16,
pub dst_call: u16,
pub retransmit: bool,
pub timestamp: u32,
pub oseq: u8,
pub iseq: u8,
pub frametype: u8,
pub subclass: u8,
pub ies: Vec<Ie>,
pub media_payload: Vec<u8>,
}
impl FullFrame {
pub fn new(
src_call: u16,
dst_call: u16,
timestamp: u32,
oseq: u8,
iseq: u8,
frametype: u8,
subclass: u8,
ies: Vec<Ie>,
) -> Self {
FullFrame {
src_call,
dst_call,
retransmit: false,
timestamp,
oseq,
iseq,
frametype,
subclass,
ies,
media_payload: Vec::new(),
}
}
fn header_into(&self, out: &mut Vec<u8>) {
let src = FULL_FRAME_BIT | (self.src_call & CALL_MASK);
let mut dst = self.dst_call & CALL_MASK;
if self.retransmit {
dst |= RETRANSMIT_BIT;
}
out.extend_from_slice(&src.to_be_bytes());
out.extend_from_slice(&dst.to_be_bytes());
out.extend_from_slice(&self.timestamp.to_be_bytes());
out.push(self.oseq);
out.push(self.iseq);
out.push(self.frametype);
out.push(self.subclass & 0x7F);
}
pub fn encode(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(12 + 32);
self.header_into(&mut out);
ie::encode(&self.ies, &mut out);
out
}
pub fn encode_media(&self, payload: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(12 + payload.len());
self.header_into(&mut out);
out.extend_from_slice(payload);
out
}
pub fn decode(buf: &[u8]) -> Option<FullFrame> {
if buf.len() < 12 {
return None;
}
let w0 = u16::from_be_bytes([buf[0], buf[1]]);
let w1 = u16::from_be_bytes([buf[2], buf[3]]);
if w0 & FULL_FRAME_BIT == 0 {
return None; }
let frametype = buf[10];
let body = &buf[12..];
let (ies, media_payload) = if is_media(frametype) {
(Vec::new(), body.to_vec())
} else {
(ie::decode(body)?, Vec::new())
};
Some(FullFrame {
src_call: w0 & CALL_MASK,
dst_call: w1 & CALL_MASK,
retransmit: w1 & RETRANSMIT_BIT != 0,
timestamp: u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]),
oseq: buf[8],
iseq: buf[9],
frametype,
subclass: buf[11] & !SUBCLASS_C_BIT,
ies,
media_payload,
})
}
}
#[derive(Debug, Clone)]
pub struct MiniFrame {
pub src_call: u16,
pub timestamp16: u16,
pub payload: Vec<u8>,
}
impl MiniFrame {
pub fn encode(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(4 + self.payload.len());
out.extend_from_slice(&(self.src_call & CALL_MASK).to_be_bytes());
out.extend_from_slice(&self.timestamp16.to_be_bytes());
out.extend_from_slice(&self.payload);
out
}
pub fn decode(buf: &[u8]) -> Option<MiniFrame> {
if buf.len() < 4 {
return None;
}
let w0 = u16::from_be_bytes([buf[0], buf[1]]);
if w0 & FULL_FRAME_BIT != 0 {
return None; }
Some(MiniFrame {
src_call: w0 & CALL_MASK,
timestamp16: u16::from_be_bytes([buf[2], buf[3]]),
payload: buf[4..].to_vec(),
})
}
}
pub fn is_full_frame(buf: &[u8]) -> bool {
buf.len() >= 2 && (buf[0] & 0x80 != 0)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::consts::{frametype, iax, ie as iec};
#[test]
fn command_frame_roundtrip() {
let f = FullFrame::new(
5, 0, 1234, 0, 0, frametype::IAX, iax::REGREQ,
vec![Ie::str(iec::USERNAME, "8001"), Ie::u16(iec::REFRESH, 60)],
);
let d = FullFrame::decode(&f.encode()).expect("decode");
assert_eq!(d.subclass, iax::REGREQ);
assert_eq!(ie::find(&d.ies, iec::USERNAME).unwrap().as_str(), "8001");
assert!(d.media_payload.is_empty());
}
#[test]
fn voice_full_frame_keeps_raw_payload() {
use crate::consts::format;
let audio = vec![0x7Fu8; 160];
let f = FullFrame::new(3, 7, 20, 1, 1, frametype::VOICE, format::ULAW as u8, vec![]);
let bytes = f.encode_media(&audio);
let d = FullFrame::decode(&bytes).expect("decode");
assert_eq!(d.frametype, frametype::VOICE);
assert_eq!(d.media_payload.len(), 160);
assert!(d.ies.is_empty(), "il payload audio non va interpretato come IE");
}
#[test]
fn mini_frame_roundtrip() {
let m = MiniFrame { src_call: 7, timestamp16: 20, payload: vec![0xAA; 160] };
let bytes = m.encode();
assert!(!is_full_frame(&bytes));
let d = MiniFrame::decode(&bytes).expect("decode");
assert_eq!(d.src_call, 7);
assert_eq!(d.payload.len(), 160);
}
}