use alloc::vec::Vec;
use broadcast_common::bits::{BitReader, BitWriter};
use broadcast_common::{Parse, Serialize};
use rtp_packet::RtpPacket;
use crate::anc_content::{AncContent, check_field_width};
use crate::error::{Error, Result};
pub const ANC_RTP_PAYLOAD_HEADER_LEN: usize = 8;
const MAX_ANC_COUNT: usize = u8::MAX as usize;
const MAX_LENGTH: usize = u16::MAX as usize;
const F_SHIFT: u8 = 6;
const F_MASK: u8 = 0b11;
const RESERVED_BYTE5_MASK: u8 = 0x3F;
pub const ANC_RTP_MEDIA_TYPE: &str = "video/smpte291";
pub const ANC_RTP_DEFAULT_CLOCK_RATE: u32 = 90_000;
const W_C: u32 = 1;
const W_LINE_NUMBER: u32 = 11;
const W_HORIZONTAL_OFFSET: u32 = 12;
const W_S: u32 = 1;
const W_STREAM_NUM: u32 = 7;
const PLACEMENT_BITS: usize =
(W_C + W_LINE_NUMBER + W_HORIZONTAL_OFFSET + W_S + W_STREAM_NUM) as usize;
fn bits_to_word_boundary(bits_so_far: usize) -> u32 {
let rem = bits_so_far % 32;
if rem == 0 { 0 } else { (32 - rem) as u32 }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum FieldSense {
ProgressiveOrUnspecified,
Invalid,
Field1,
Field2,
}
impl FieldSense {
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::ProgressiveOrUnspecified => "progressive or unspecified",
Self::Invalid => "invalid (0b01)",
Self::Field1 => "field 1",
Self::Field2 => "field 2",
}
}
fn from_bits(bits: u8) -> Self {
match bits & F_MASK {
0b00 => Self::ProgressiveOrUnspecified,
0b01 => Self::Invalid,
0b10 => Self::Field1,
_ => Self::Field2,
}
}
fn to_bits(self) -> u8 {
match self {
Self::ProgressiveOrUnspecified => 0b00,
Self::Invalid => 0b01,
Self::Field1 => 0b10,
Self::Field2 => 0b11,
}
}
}
broadcast_common::impl_spec_display!(FieldSense);
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct RtpAncPacket {
pub c: bool,
pub line_number: u16,
pub horizontal_offset: u16,
pub s: bool,
pub stream_num: u8,
pub content: AncContent,
}
impl RtpAncPacket {
fn body_bits(&self) -> usize {
PLACEMENT_BITS + self.content.content_bit_width()
}
#[must_use]
pub fn serialized_len(&self) -> usize {
let bits = self.body_bits();
(bits + bits_to_word_boundary(bits) as usize) / 8
}
fn write_into(&self, w: &mut BitWriter<'_>) -> Result<()> {
w.write_bool(self.c)?;
w.write_bits(
check_field_width("Line_Number", u64::from(self.line_number), W_LINE_NUMBER)?,
W_LINE_NUMBER,
)?;
w.write_bits(
check_field_width(
"Horizontal_Offset",
u64::from(self.horizontal_offset),
W_HORIZONTAL_OFFSET,
)?,
W_HORIZONTAL_OFFSET,
)?;
w.write_bool(self.s)?;
w.write_bits(
check_field_width("StreamNum", u64::from(self.stream_num), W_STREAM_NUM)?,
W_STREAM_NUM,
)?;
self.content.write_into(w)?;
let pad = bits_to_word_boundary(w.bits_written());
w.write_bits(0, pad)?;
Ok(())
}
fn read_from(r: &mut BitReader<'_>) -> Result<Self> {
let c = r.read_bool()?;
let line_number = r.read_bits(W_LINE_NUMBER)? as u16;
let horizontal_offset = r.read_bits(W_HORIZONTAL_OFFSET)? as u16;
let s = r.read_bool()?;
let stream_num = r.read_bits(W_STREAM_NUM)? as u8;
let content = AncContent::read_from(r)?;
let pad = bits_to_word_boundary(r.bits_read());
if pad > 0 {
let pad_value = r.read_bits(pad)?;
if pad_value != 0 {
return Err(Error::ReservedNotZero {
what: "word_align",
value: pad_value,
});
}
}
Ok(Self {
c,
line_number,
horizontal_offset,
s,
stream_num,
content,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct AncRtpPayload {
pub extended_sequence_number: u16,
pub field_sense: FieldSense,
pub anc_packets: Vec<RtpAncPacket>,
}
impl AncRtpPayload {
#[must_use]
pub fn anc_count(&self) -> usize {
self.anc_packets.len()
}
fn body_len(&self) -> usize {
self.anc_packets
.iter()
.map(RtpAncPacket::serialized_len)
.sum()
}
pub fn parse_rtp_packet(bytes: &[u8]) -> Result<(RtpPacket<'_>, Self)> {
let rtp = RtpPacket::parse(bytes).map_err(Error::Rtp)?;
let payload = Self::parse(rtp.payload)?;
Ok((rtp, payload))
}
}
impl<'a> Parse<'a> for AncRtpPayload {
type Error = Error;
fn parse(b: &'a [u8]) -> Result<Self> {
if b.len() < ANC_RTP_PAYLOAD_HEADER_LEN {
return Err(Error::BufferTooShort {
need: ANC_RTP_PAYLOAD_HEADER_LEN,
have: b.len(),
what: "ANC RTP payload header",
});
}
let extended_sequence_number = u16::from_be_bytes([b[0], b[1]]);
let length = usize::from(u16::from_be_bytes([b[2], b[3]]));
let anc_count = usize::from(b[4]);
let f_bits = b[5] >> F_SHIFT;
let reserved = (u32::from(b[5] & RESERVED_BYTE5_MASK) << 16)
| (u32::from(b[6]) << 8)
| u32::from(b[7]);
if reserved != 0 {
return Err(Error::ReservedNotZero {
what: "reserved (RFC 8331 §2.1)",
value: u64::from(reserved),
});
}
let field_sense = FieldSense::from_bits(f_bits);
let end = ANC_RTP_PAYLOAD_HEADER_LEN + length;
if b.len() < end {
return Err(Error::BufferTooShort {
need: end,
have: b.len(),
what: "ANC RTP payload body",
});
}
let body = &b[ANC_RTP_PAYLOAD_HEADER_LEN..end];
let mut r = BitReader::new(body);
let mut anc_packets = Vec::with_capacity(anc_count);
for _ in 0..anc_count {
anc_packets.push(RtpAncPacket::read_from(&mut r)?);
}
let consumed_bytes = r.bits_read() / 8;
if !r.is_byte_aligned() || consumed_bytes != body.len() {
return Err(Error::LengthMismatch {
declared: length,
computed: consumed_bytes,
});
}
Ok(Self {
extended_sequence_number,
field_sense,
anc_packets,
})
}
}
impl Serialize for AncRtpPayload {
type Error = Error;
fn serialized_len(&self) -> usize {
ANC_RTP_PAYLOAD_HEADER_LEN + self.body_len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len();
if buf.len() < len {
return Err(Error::BufferTooShort {
need: len,
have: buf.len(),
what: "ANC RTP payload serialize output",
});
}
let anc_count = self.anc_count();
if anc_count > MAX_ANC_COUNT {
return Err(Error::FieldTooWide {
what: "ANC_Count",
value: anc_count as u32,
bits: 8,
});
}
let body_len = self.body_len();
if body_len > MAX_LENGTH {
return Err(Error::FieldTooWide {
what: "Length",
value: body_len as u32,
bits: 16,
});
}
buf[0..2].copy_from_slice(&self.extended_sequence_number.to_be_bytes());
buf[2..4].copy_from_slice(&(body_len as u16).to_be_bytes());
buf[4] = anc_count as u8;
buf[5] = self.field_sense.to_bits() << F_SHIFT; buf[6] = 0;
buf[7] = 0;
let mut pos = ANC_RTP_PAYLOAD_HEADER_LEN;
for pkt in &self.anc_packets {
let pkt_len = pkt.serialized_len();
let mut w = BitWriter::new(&mut buf[pos..pos + pkt_len]);
pkt.write_into(&mut w)?;
pos += pkt_len;
}
Ok(len)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
use alloc::vec;
fn sample_payload() -> AncRtpPayload {
AncRtpPayload {
extended_sequence_number: 0x0001,
field_sense: FieldSense::ProgressiveOrUnspecified,
anc_packets: vec![
RtpAncPacket {
c: false,
line_number: 9,
horizontal_offset: 0,
s: false,
stream_num: 0,
content: AncContent {
did: 0x161,
sdid: 0x101,
data_count: 0x002,
user_data_words: vec![0x2CF, 0x101],
checksum: 0x233,
},
},
RtpAncPacket {
c: true,
line_number: 10,
horizontal_offset: 0x10,
s: false,
stream_num: 0,
content: AncContent {
did: 0x241,
sdid: 0x102,
data_count: 0x003,
user_data_words: vec![0x111, 0x222, 0x333],
checksum: 0x1AB,
},
},
],
}
}
#[test]
fn round_trip() {
let p = sample_payload();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
let reparsed = AncRtpPayload::parse(&out).unwrap();
assert_eq!(reparsed, p);
}
#[test]
fn anc_count_and_length_recomputed_on_serialize() {
let p = sample_payload();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
assert_eq!(out[4], 2, "ANC_Count derived from anc_packets.len()");
let declared_length = u16::from_be_bytes([out[2], out[3]]) as usize;
assert_eq!(declared_length, out.len() - ANC_RTP_PAYLOAD_HEADER_LEN);
}
#[test]
fn empty_payload_has_zero_count_and_length() {
let p = AncRtpPayload {
extended_sequence_number: 0,
field_sense: FieldSense::ProgressiveOrUnspecified,
anc_packets: vec![],
};
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
assert_eq!(out.len(), ANC_RTP_PAYLOAD_HEADER_LEN);
assert_eq!(out[4], 0);
assert_eq!(&out[2..4], &[0, 0]);
assert_eq!(AncRtpPayload::parse(&out).unwrap(), p);
}
#[test]
fn field_mutation_changes_bytes() {
let a = sample_payload();
let mut b = a.clone();
b.anc_packets[0].content.user_data_words[0] = 0x000;
let mut oa = vec![0u8; a.serialized_len()];
let mut ob = vec![0u8; b.serialized_len()];
a.serialize_into(&mut oa).unwrap();
b.serialize_into(&mut ob).unwrap();
assert_ne!(oa, ob, "changing a UDW must change the wire bytes");
let mut c = a.clone();
c.anc_packets[1].line_number = 11;
let mut oc = vec![0u8; c.serialized_len()];
c.serialize_into(&mut oc).unwrap();
assert_ne!(oa, oc, "changing line_number must change the wire bytes");
let mut d = a.clone();
d.anc_packets[0].stream_num = 5;
d.anc_packets[0].s = true;
let mut od = vec![0u8; d.serialized_len()];
d.serialize_into(&mut od).unwrap();
assert_ne!(oa, od, "changing S/StreamNum must change the wire bytes");
}
#[test]
fn rejects_corrupted_length_too_small() {
let p = sample_payload();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
let good_length = u16::from_be_bytes([out[2], out[3]]);
let bad_length = good_length - 4;
out[2..4].copy_from_slice(&bad_length.to_be_bytes());
assert!(AncRtpPayload::parse(&out).is_err());
}
#[test]
fn rejects_corrupted_length_leaves_leftover_bytes() {
let p = sample_payload();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
let good_length = u16::from_be_bytes([out[2], out[3]]);
let bad_length = good_length + 4;
out[2..4].copy_from_slice(&bad_length.to_be_bytes());
out.extend_from_slice(&[0, 0, 0, 0]);
assert!(matches!(
AncRtpPayload::parse(&out),
Err(Error::LengthMismatch { .. })
));
}
#[test]
fn rejects_corrupted_anc_count() {
let p = sample_payload();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
out[4] = 3; assert!(AncRtpPayload::parse(&out).is_err());
}
#[test]
fn rejects_nonzero_reserved() {
let p = sample_payload();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
out[7] = 0x01; assert!(matches!(
AncRtpPayload::parse(&out),
Err(Error::ReservedNotZero { .. })
));
}
#[test]
fn rejects_nonzero_word_align_padding() {
let p = sample_payload();
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
let last = out.len() - 1;
out[last] |= 0x01;
assert!(matches!(
AncRtpPayload::parse(&out),
Err(Error::ReservedNotZero {
what: "word_align",
..
})
));
}
#[test]
fn field_sense_all_four_values_round_trip() {
for (bits, expect) in [
(0b00u8, FieldSense::ProgressiveOrUnspecified),
(0b01, FieldSense::Invalid),
(0b10, FieldSense::Field1),
(0b11, FieldSense::Field2),
] {
let mut p = sample_payload();
p.field_sense = FieldSense::from_bits(bits);
assert_eq!(p.field_sense, expect);
let mut out = vec![0u8; p.serialized_len()];
p.serialize_into(&mut out).unwrap();
assert_eq!(out[5] >> F_SHIFT, bits);
let reparsed = AncRtpPayload::parse(&out).unwrap();
assert_eq!(reparsed.field_sense, expect);
}
}
#[test]
fn field_sense_labels() {
assert_eq!(
FieldSense::ProgressiveOrUnspecified.name(),
"progressive or unspecified"
);
assert_eq!(FieldSense::Invalid.name(), "invalid (0b01)");
assert_eq!(FieldSense::Field1.name(), "field 1");
assert_eq!(FieldSense::Field2.name(), "field 2");
assert_eq!(FieldSense::Invalid.to_string(), "invalid (0b01)");
}
#[test]
fn rejects_field_too_wide_stream_num() {
let mut p = sample_payload();
p.anc_packets[0].stream_num = 0x7F + 1; let mut out = vec![0u8; p.serialized_len()];
assert!(matches!(
p.serialize_into(&mut out),
Err(Error::FieldTooWide {
what: "StreamNum",
..
})
));
}
#[test]
fn parse_rtp_packet_composition() {
let anc_payload = sample_payload();
let mut anc_bytes = vec![0u8; anc_payload.serialized_len()];
anc_payload.serialize_into(&mut anc_bytes).unwrap();
let rtp = RtpPacket {
marker: true,
payload_type: 112, sequence_number: 1,
timestamp: ANC_RTP_DEFAULT_CLOCK_RATE,
ssrc: 0xCAFE_BABE,
csrc: Vec::new(),
extension: None,
padding: None,
payload: &anc_bytes,
};
let mut rtp_bytes = vec![0u8; rtp.serialized_len()];
rtp.serialize_into(&mut rtp_bytes).unwrap();
let (parsed_rtp, parsed_anc) = AncRtpPayload::parse_rtp_packet(&rtp_bytes).unwrap();
assert!(parsed_rtp.marker);
assert_eq!(parsed_rtp.payload_type, 112);
assert_eq!(parsed_anc, anc_payload);
}
#[test]
fn parse_rtp_packet_rejects_bad_rtp_header() {
let mut bytes = vec![0u8; rtp_packet::FIXED_HEADER_LEN + ANC_RTP_PAYLOAD_HEADER_LEN];
bytes[0] = 0x00; assert!(matches!(
AncRtpPayload::parse_rtp_packet(&bytes),
Err(Error::Rtp(_))
));
}
#[test]
fn rejects_too_many_anc_packets() {
let one = &sample_payload().anc_packets[0];
let mut p = AncRtpPayload {
extended_sequence_number: 0,
field_sense: FieldSense::ProgressiveOrUnspecified,
anc_packets: Vec::new(),
};
for _ in 0..=MAX_ANC_COUNT {
p.anc_packets.push(one.clone());
}
let mut out = vec![0u8; p.serialized_len()];
assert!(matches!(
p.serialize_into(&mut out),
Err(Error::FieldTooWide {
what: "ANC_Count",
..
})
));
}
}