#![allow(dead_code)]
use std::collections::HashMap;
use crate::error::{VideoIpError, VideoIpResult};
pub const MAX_PAYLOAD_BYTES: usize = 1400;
pub const YUV422_8BIT_BYTES_PER_2PIX: usize = 4;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct St2110_20Packet {
pub seq_num: u16,
pub timestamp: u32,
pub line_num: u16,
pub offset: u16,
pub continuation: bool,
pub payload: Vec<u8>,
}
impl St2110_20Packet {
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(12 + self.payload.len());
out.extend_from_slice(&self.seq_num.to_be_bytes());
out.extend_from_slice(&self.timestamp.to_be_bytes());
out.extend_from_slice(&self.line_num.to_be_bytes());
out.extend_from_slice(&self.offset.to_be_bytes());
let flags: u8 = if self.continuation { 0x80 } else { 0x00 };
out.push(flags);
out.push(0x00); out.extend_from_slice(&self.payload);
out
}
#[must_use]
pub fn from_bytes(data: &[u8]) -> Option<Self> {
if data.len() < 12 {
return None;
}
let seq_num = u16::from_be_bytes([data[0], data[1]]);
let timestamp = u32::from_be_bytes([data[2], data[3], data[4], data[5]]);
let line_num = u16::from_be_bytes([data[6], data[7]]);
let offset = u16::from_be_bytes([data[8], data[9]]);
let continuation = (data[10] & 0x80) != 0;
let payload = data[12..].to_vec();
Some(Self {
seq_num,
timestamp,
line_num,
offset,
continuation,
payload,
})
}
}
#[derive(Debug, Clone)]
pub struct St2110_20Sender {
pub timestamp_increment: u32,
next_seq: u16,
next_timestamp: u32,
}
impl St2110_20Sender {
#[must_use]
pub fn new(timestamp_increment: u32) -> Self {
Self {
timestamp_increment,
next_seq: 0,
next_timestamp: 0,
}
}
#[must_use]
pub fn for_fps(fps_num: u32, fps_den: u32) -> Self {
let increment = (90_000u64 * u64::from(fps_den)) / u64::from(fps_num);
Self::new(increment as u32)
}
pub fn send_frame(
&mut self,
yuv422: &[u8],
width: u32,
height: u32,
) -> VideoIpResult<Vec<St2110_20Packet>> {
let expected = (width as usize) * (height as usize) * 2;
if yuv422.len() != expected {
return Err(VideoIpError::InvalidPacket(format!(
"expected {expected} bytes for {width}×{height} YUV422, got {}",
yuv422.len()
)));
}
let bytes_per_line = (width as usize) * 2; let ts = self.next_timestamp;
self.next_timestamp = self.next_timestamp.wrapping_add(self.timestamp_increment);
let mut packets: Vec<St2110_20Packet> = Vec::new();
for line in 0..height {
let line_start = (line as usize) * bytes_per_line;
let line_data = &yuv422[line_start..line_start + bytes_per_line];
let chunks: Vec<&[u8]> = line_data.chunks(MAX_PAYLOAD_BYTES).collect();
let num_chunks = chunks.len();
for (chunk_idx, chunk) in chunks.into_iter().enumerate() {
let byte_offset = chunk_idx * MAX_PAYLOAD_BYTES;
let pixel_offset = (byte_offset / 2) as u16;
let more_chunks = chunk_idx + 1 < num_chunks;
let more_lines = line + 1 < height;
let continuation = more_chunks || more_lines;
let pkt = St2110_20Packet {
seq_num: self.next_seq,
timestamp: ts,
line_num: line as u16,
offset: pixel_offset,
continuation,
payload: chunk.to_vec(),
};
self.next_seq = self.next_seq.wrapping_add(1);
packets.push(pkt);
}
}
Ok(packets)
}
}
#[derive(Debug, Default)]
struct FrameBuffer {
lines: HashMap<u16, Vec<u8>>,
expected_lines: Option<u16>,
width_pixels: Option<u16>,
}
impl FrameBuffer {
fn insert_chunk(&mut self, line_num: u16, offset: u16, payload: &[u8]) {
let byte_offset = (offset as usize) * 2;
let entry = self.lines.entry(line_num).or_default();
let needed = byte_offset + payload.len();
if entry.len() < needed {
entry.resize(needed, 0u8);
}
entry[byte_offset..byte_offset + payload.len()].copy_from_slice(payload);
}
fn is_complete(&self) -> bool {
match (self.expected_lines, self.width_pixels) {
(Some(h), Some(w)) => {
let bytes_per_line = (w as usize) * 2;
(0..h).all(|l| {
self.lines
.get(&l)
.map_or(false, |v| v.len() == bytes_per_line)
})
}
_ => false,
}
}
fn assemble(&self) -> Option<Vec<u8>> {
let height = self.expected_lines?;
let width = self.width_pixels?;
let bytes_per_line = (width as usize) * 2;
let mut out = Vec::with_capacity((height as usize) * bytes_per_line);
for l in 0..height {
let line = self.lines.get(&l)?;
if line.len() != bytes_per_line {
return None;
}
out.extend_from_slice(line);
}
Some(out)
}
}
#[derive(Debug, Default)]
pub struct St2110_20Receiver {
buffers: HashMap<u32, FrameBuffer>,
width: u16,
height: u16,
}
impl St2110_20Receiver {
#[must_use]
pub fn new(width: u16, height: u16) -> Self {
Self {
buffers: HashMap::new(),
width,
height,
}
}
pub fn receive_packet(&mut self, pkt: &St2110_20Packet) -> Option<Vec<u8>> {
let buf = self.buffers.entry(pkt.timestamp).or_default();
if buf.expected_lines.is_none() {
buf.expected_lines = Some(self.height);
buf.width_pixels = Some(self.width);
}
buf.insert_chunk(pkt.line_num, pkt.offset, &pkt.payload);
if buf.is_complete() {
let frame = buf.assemble();
self.buffers.remove(&pkt.timestamp);
frame
} else {
None
}
}
#[must_use]
pub fn pending_frames(&self) -> usize {
self.buffers.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_frame(width: u32, height: u32) -> Vec<u8> {
let len = (width * height * 2) as usize;
(0..len).map(|i| (i % 256) as u8).collect()
}
#[test]
fn test_packet_roundtrip_serialisation() {
let pkt = St2110_20Packet {
seq_num: 0xABCD,
timestamp: 0x1234_5678,
line_num: 42,
offset: 100,
continuation: true,
payload: vec![1, 2, 3, 4, 5],
};
let bytes = pkt.to_bytes();
let decoded = St2110_20Packet::from_bytes(&bytes).expect("decode must succeed");
assert_eq!(decoded, pkt);
}
#[test]
fn test_packet_continuation_flag_false() {
let pkt = St2110_20Packet {
seq_num: 1,
timestamp: 2,
line_num: 0,
offset: 0,
continuation: false,
payload: vec![0xFF],
};
let bytes = pkt.to_bytes();
let decoded = St2110_20Packet::from_bytes(&bytes).expect("decode");
assert!(!decoded.continuation);
}
#[test]
fn test_packet_from_bytes_too_short() {
let data = [0u8; 5];
assert!(St2110_20Packet::from_bytes(&data).is_none());
}
#[test]
fn test_packet_serialised_length() {
let pkt = St2110_20Packet {
seq_num: 0,
timestamp: 0,
line_num: 0,
offset: 0,
continuation: false,
payload: vec![0u8; 100],
};
assert_eq!(pkt.to_bytes().len(), 12 + 100);
}
#[test]
fn test_packet_empty_payload() {
let pkt = St2110_20Packet {
seq_num: 7,
timestamp: 99,
line_num: 3,
offset: 0,
continuation: false,
payload: vec![],
};
let bytes = pkt.to_bytes();
assert_eq!(bytes.len(), 12);
let decoded = St2110_20Packet::from_bytes(&bytes).expect("decode");
assert_eq!(decoded.seq_num, 7);
assert!(decoded.payload.is_empty());
}
#[test]
fn test_sender_small_frame_packet_count() {
let mut sender = St2110_20Sender::new(3000);
let frame = make_frame(4, 2);
let pkts = sender.send_frame(&frame, 4, 2).expect("send");
assert_eq!(pkts.len(), 2, "one packet per line");
}
#[test]
fn test_sender_wrong_buffer_size_error() {
let mut sender = St2110_20Sender::new(3000);
let frame = vec![0u8; 100]; assert!(sender.send_frame(&frame, 4, 2).is_err());
}
#[test]
fn test_sender_sequence_numbers_increment() {
let mut sender = St2110_20Sender::new(3000);
let frame = make_frame(4, 4);
let pkts = sender.send_frame(&frame, 4, 4).expect("send");
for (i, pkt) in pkts.iter().enumerate() {
assert_eq!(pkt.seq_num, i as u16);
}
}
#[test]
fn test_sender_timestamp_increments_each_frame() {
let mut sender = St2110_20Sender::new(3000);
let frame = make_frame(4, 2);
let pkts1 = sender.send_frame(&frame, 4, 2).expect("frame 1");
let pkts2 = sender.send_frame(&frame, 4, 2).expect("frame 2");
assert_eq!(pkts1[0].timestamp + 3000, pkts2[0].timestamp);
}
#[test]
fn test_sender_line_numbers_correct() {
let mut sender = St2110_20Sender::new(3000);
let frame = make_frame(4, 3);
let pkts = sender.send_frame(&frame, 4, 3).expect("send");
let line_nums: Vec<u16> = pkts.iter().map(|p| p.line_num).collect();
assert_eq!(line_nums, vec![0, 1, 2]);
}
#[test]
fn test_sender_last_packet_continuation_false() {
let mut sender = St2110_20Sender::new(3000);
let frame = make_frame(4, 2);
let pkts = sender.send_frame(&frame, 4, 2).expect("send");
assert!(!pkts.last().expect("last pkt").continuation);
}
#[test]
fn test_sender_large_line_splits_into_multiple_packets() {
let mut sender = St2110_20Sender::new(3000);
let frame = make_frame(1920, 1);
let pkts = sender.send_frame(&frame, 1920, 1).expect("send");
assert_eq!(pkts.len(), 3);
}
#[test]
fn test_sender_pixel_offsets_in_split_packets() {
let mut sender = St2110_20Sender::new(3000);
let frame = make_frame(1920, 1);
let pkts = sender.send_frame(&frame, 1920, 1).expect("send");
assert_eq!(pkts[0].offset, 0);
assert_eq!(pkts[1].offset, 700);
assert_eq!(pkts[2].offset, 1400);
}
#[test]
fn test_roundtrip_small_frame() {
let width: u32 = 4;
let height: u32 = 2;
let original = make_frame(width, height);
let mut sender = St2110_20Sender::new(3000);
let mut receiver = St2110_20Receiver::new(width as u16, height as u16);
let pkts = sender.send_frame(&original, width, height).expect("send");
let mut result: Option<Vec<u8>> = None;
for pkt in &pkts {
result = receiver.receive_packet(pkt);
}
let frame = result.expect("complete frame");
assert_eq!(frame, original);
}
#[test]
fn test_roundtrip_hd_frame() {
let width: u32 = 1920;
let height: u32 = 1080;
let original = make_frame(width, height);
let mut sender = St2110_20Sender::new(3000);
let mut receiver = St2110_20Receiver::new(width as u16, height as u16);
let pkts = sender.send_frame(&original, width, height).expect("send");
let mut result: Option<Vec<u8>> = None;
for pkt in &pkts {
result = receiver.receive_packet(pkt);
}
assert_eq!(result.expect("complete frame"), original);
}
#[test]
fn test_receiver_incomplete_frame_returns_none() {
let mut receiver = St2110_20Receiver::new(4, 4);
let pkt = St2110_20Packet {
seq_num: 0,
timestamp: 1000,
line_num: 0,
offset: 0,
continuation: true,
payload: vec![0u8; 8], };
assert!(receiver.receive_packet(&pkt).is_none());
assert_eq!(receiver.pending_frames(), 1);
}
#[test]
fn test_receiver_multiple_timestamps_independent() {
let width: u32 = 4;
let height: u32 = 2;
let frame_a = make_frame(width, height);
let frame_b: Vec<u8> = (0..(width * height * 2) as usize)
.map(|i| ((i + 10) % 256) as u8)
.collect();
let mut sender_a = St2110_20Sender::new(3000);
let mut sender_b = St2110_20Sender::new(3000);
sender_b.next_timestamp = 9000;
let pkts_a = sender_a.send_frame(&frame_a, width, height).expect("a");
let pkts_b = sender_b.send_frame(&frame_b, width, height).expect("b");
let mut receiver = St2110_20Receiver::new(width as u16, height as u16);
for pkt in pkts_a.iter().zip(pkts_b.iter()).flat_map(|(a, b)| [a, b]) {
receiver.receive_packet(pkt);
}
let mut receiver2 = St2110_20Receiver::new(width as u16, height as u16);
let mut got_a = None;
let mut got_b = None;
for pkt in &pkts_a {
got_a = receiver2.receive_packet(pkt).or(got_a);
}
for pkt in &pkts_b {
got_b = receiver2.receive_packet(pkt).or(got_b);
}
assert_eq!(got_a.expect("frame A"), frame_a);
assert_eq!(got_b.expect("frame B"), frame_b);
}
#[test]
fn test_for_fps_30() {
let sender = St2110_20Sender::for_fps(30, 1);
assert_eq!(sender.timestamp_increment, 3000);
}
#[test]
fn test_for_fps_25() {
let sender = St2110_20Sender::for_fps(25, 1);
assert_eq!(sender.timestamp_increment, 3600);
}
}