use serde::{Deserialize, Serialize};
use super::wire::{Tracks, VideoCodec, VideoHeader, VideoKind};
pub const KIND_FRAME: u8 = 1;
pub const KIND_CONTROL: u8 = 2;
pub const KIND_PCM: u8 = 3;
pub const PCM_HEADER_LEN: usize = 5;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "t", rename_all = "snake_case")]
pub enum ToLink {
Rate { kind: VideoKind, kbps: u32, width: u32, height: u32, fps: u32 },
Skip { kind: VideoKind },
Keyframe { kind: VideoKind },
Peer { tracks: Tracks },
Pause { on: bool },
Codec { codec: VideoCodec },
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "t", rename_all = "snake_case")]
pub enum FromLink {
Caps { encode: Vec<String>, decode: Vec<String> },
Stats { kind: VideoKind, fps: u32, kbps: u32, width: u32, height: u32 },
Lost { kind: VideoKind },
Unsupported { codec: VideoCodec },
#[serde(other)]
Unknown,
}
pub enum LinkMsg<'a> {
Frame { header: VideoHeader, frame: &'a [u8] },
Control(FromLink),
Pcm { rate: u32, channels: u8, samples: &'a [u8] },
}
pub fn parse(msg: &[u8]) -> Option<LinkMsg<'_>> {
match msg.first()? {
&KIND_FRAME => {
let frame = &msg[1..];
let header = VideoHeader::parse(frame)?;
Some(LinkMsg::Frame { header, frame })
}
&KIND_CONTROL => serde_json::from_slice(&msg[1..]).ok().map(LinkMsg::Control),
&KIND_PCM => {
let body = &msg[1..];
if body.len() < PCM_HEADER_LEN {
return None;
}
let rate = u32::from_le_bytes([body[0], body[1], body[2], body[3]]);
let channels = body[4];
if !(1..=2).contains(&channels) || !(8000..=192_000).contains(&rate) {
return None;
}
Some(LinkMsg::Pcm { rate, channels, samples: &body[PCM_HEADER_LEN..] })
}
_ => None,
}
}
pub fn control(msg: &ToLink) -> Vec<u8> {
let mut out = vec![KIND_CONTROL];
if let Ok(body) = serde_json::to_vec(msg) {
out.extend_from_slice(&body);
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::wire::{VIDEO_HEADER_LEN, VIDEO_KEY};
#[test]
fn frames_and_controls_parse_and_garbage_does_not() {
let mut msg = vec![KIND_FRAME];
let mut hdr = [0u8; VIDEO_HEADER_LEN];
VideoHeader { seq: 3, ts_us: 9, flags: VIDEO_KEY, codec: VideoCodec::H264 }.write(&mut hdr);
msg.extend_from_slice(&hdr);
msg.extend_from_slice(&[7, 7, 7]);
match parse(&msg) {
Some(LinkMsg::Frame { header, frame }) => {
assert_eq!(header.seq, 3);
assert_eq!(frame.len(), VIDEO_HEADER_LEN + 3);
}
_ => panic!("frame expected"),
}
let c = control(&ToLink::Rate { kind: VideoKind::Camera, kbps: 800, width: 640, height: 360, fps: 30 });
assert_eq!(c[0], KIND_CONTROL);
let mut lost = vec![KIND_CONTROL];
lost.extend_from_slice(br#"{"t":"lost","kind":"screen"}"#);
assert!(matches!(parse(&lost), Some(LinkMsg::Control(FromLink::Lost { kind: VideoKind::Screen }))));
let mut odd = vec![KIND_CONTROL];
odd.extend_from_slice(br#"{"t":"teleport"}"#);
assert!(matches!(parse(&odd), Some(LinkMsg::Control(FromLink::Unknown))));
assert!(parse(&[9, 9]).is_none());
assert!(parse(&[]).is_none());
let mut pcm = vec![KIND_PCM];
pcm.extend_from_slice(&48_000u32.to_le_bytes());
pcm.push(2);
pcm.extend_from_slice(&1.0f32.to_le_bytes());
match parse(&pcm) {
Some(LinkMsg::Pcm { rate, channels, samples }) => assert_eq!((rate, channels, samples.len()), (48_000, 2, 4)),
_ => panic!("pcm expected"),
}
pcm[5] = 7;
assert!(parse(&pcm).is_none(), "an impossible channel count is refused");
}
}