vector-calls 0.1.0

The platform-free half of Vector's calls: wire format, jitter buffer, rate control.
Documentation
//! The link between the media engine and whatever captures, encodes, decodes and
//! paints video (a webview over a loopback socket, or an in-process port). Binary
//! messages, the first byte says what follows.

use serde::{Deserialize, Serialize};

use super::wire::{Tracks, VideoCodec, VideoHeader, VideoKind};

/// A video frame: the 16-byte wire header, then the encoded bytes. The same bytes
/// go on the QUIC stream, so nothing is re-framed in between.
pub const KIND_FRAME: u8 = 1;
/// A JSON control message.
pub const KIND_CONTROL: u8 = 2;
/// Raw audio from a screen share: a 5-byte header (sample rate u32, channels u8) and
/// interleaved little-endian f32 samples. Cancelled and encoded on the other side.
pub const KIND_PCM: u8 = 3;
pub const PCM_HEADER_LEN: usize = 5;

/// Sent to the encoder side of the link.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "t", rename_all = "snake_case")]
pub enum ToLink {
    /// Encode that track at this rate, size and frame rate from now on.
    Rate { kind: VideoKind, kbps: u32, width: u32, height: u32, fps: u32 },
    /// Too many frames in flight: drop that track's next capture.
    Skip { kind: VideoKind },
    /// Make that track's next frame a keyframe.
    Keyframe { kind: VideoKind },
    /// What the peer is sending now, so the stage can show or hide their pictures.
    Peer { tracks: Tracks },
    /// The peer cannot see us; stop capturing (or resume).
    Pause { on: bool },
    /// Send with this codec: the one the peer decodes.
    Codec { codec: VideoCodec },
}

/// Received from the encoder side of the link.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "t", rename_all = "snake_case")]
pub enum FromLink {
    /// What this device can encode and decode, from its boot probe.
    Caps { encode: Vec<String>, decode: Vec<String> },
    /// One encoder's own numbers, for the stats line.
    Stats { kind: VideoKind, fps: u32, kbps: u32, width: u32, height: u32 },
    /// My decoder for that track lost the chain: ask the peer for a keyframe.
    Lost { kind: VideoKind },
    /// My decoder refuses this codec outright: ask the peer to send another.
    Unsupported { codec: VideoCodec },
    #[serde(other)]
    Unknown,
}

/// What one link message is.
pub enum LinkMsg<'a> {
    Frame { header: VideoHeader, frame: &'a [u8] },
    Control(FromLink),
    Pcm { rate: u32, channels: u8, samples: &'a [u8] },
}

/// Parses a message from the encoder side. `frame` is the header plus payload, the
/// exact bytes to put on the wire.
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,
    }
}

/// A control message ready to send to the encoder side.
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");
    }
}