use super::{BitRead, CodecExtra, Depacketizer, PacketError, Packetizer, encode_leb_u63};
const OBU_EXTENSION_PRESENT_MASK: u8 = 0b0000_0100;
const OBU_SIZE_PRESENT_MASK: u8 = 0b0000_0010;
const OBU_TYPE_MASK: u8 = 0b0111_1000;
const AGGREGATION_HEADER_SIZE: usize = 1;
const MAX_NUM_OBUS_TO_OMTI_SIZE: usize = 3;
pub fn detect_av1_keyframe(payload: &[u8]) -> bool {
if payload.is_empty() {
return false;
}
payload[0] & 0x08 != 0
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Av1CodecExtra {
pub is_keyframe: bool,
}
#[derive(Default, Debug)]
pub struct Av1Packetizer {
packets: Vec<Packet>,
obus: Vec<Obu>,
}
impl Av1Packetizer {
fn emit(&mut self, mtu: usize, payloads: &mut Vec<Vec<u8>>) {
if self.obus.is_empty() {
return;
}
self.packets.clear();
let max_payload_size = mtu - AGGREGATION_HEADER_SIZE;
let mut remaining_packet_size = max_payload_size;
let mut current_packet = Packet::new(0);
for (obu_idx, obu) in self.obus.iter().enumerate() {
let mut previous_obu_extra_size = self.extra_size_for_previous_obu(¤t_packet);
let min_required_size = if current_packet.num_obu_elements >= MAX_NUM_OBUS_TO_OMTI_SIZE
{
2
} else {
1
};
if remaining_packet_size < previous_obu_extra_size + min_required_size {
self.packets.push(current_packet);
current_packet = Packet::new(obu_idx);
remaining_packet_size = max_payload_size;
previous_obu_extra_size = 0;
}
current_packet.packet_size += previous_obu_extra_size;
current_packet.num_obu_elements += 1;
remaining_packet_size -= previous_obu_extra_size;
let must_write_obu_element_size =
current_packet.num_obu_elements > MAX_NUM_OBUS_TO_OMTI_SIZE;
let mut required_bytes = obu.size;
if must_write_obu_element_size {
required_bytes += leb_128_size(obu.size);
}
if required_bytes <= remaining_packet_size {
current_packet.last_obu_size = obu.size;
current_packet.packet_size += required_bytes;
remaining_packet_size -= required_bytes;
continue;
}
let max_first_fragment_size = match must_write_obu_element_size {
true => self.max_fragment_size(remaining_packet_size),
false => remaining_packet_size,
};
if max_first_fragment_size == 0 {
current_packet.num_obu_elements -= 1;
current_packet.packet_size -= previous_obu_extra_size;
} else {
current_packet.packet_size += max_first_fragment_size;
if must_write_obu_element_size {
current_packet.packet_size += leb_128_size(max_first_fragment_size);
}
current_packet.last_obu_size = max_first_fragment_size;
}
let mut obu_offset = max_first_fragment_size;
while obu_offset + max_payload_size < obu.size {
self.packets.push(current_packet);
current_packet = Packet::new(obu_idx);
current_packet.num_obu_elements = 1;
current_packet.first_obu_offset = obu_offset;
current_packet.last_obu_size = max_payload_size;
current_packet.packet_size = max_payload_size;
obu_offset += max_payload_size
}
let last_fragment_size = obu.size - obu_offset;
self.packets.push(current_packet);
current_packet = Packet::new(obu_idx);
current_packet.num_obu_elements = 1;
current_packet.first_obu_offset = obu_offset;
current_packet.last_obu_size = last_fragment_size;
current_packet.packet_size = last_fragment_size;
remaining_packet_size = max_payload_size - last_fragment_size;
}
self.packets.push(current_packet);
self.write_rtp_payloads(payloads)
}
fn write_rtp_payloads(&self, payloads: &mut Vec<Vec<u8>>) {
for (i, packet) in self.packets.iter().enumerate() {
let is_first_packet = i == 0;
let mut rtp_payload: Vec<u8> = vec![0u8; AGGREGATION_HEADER_SIZE + packet.packet_size];
let mut pos = 0;
let header = self.aggregation_header(packet, &self.obus, is_first_packet);
rtp_payload[pos] = header;
pos += 1;
let mut obu_offset = packet.first_obu_offset;
for obu_idx in 0..(packet.num_obu_elements - 1) {
let obu = &self.obus[packet.first_obu + obu_idx];
let obu_fragment_size = obu.size - obu_offset;
pos += encode_leb_u63(obu_fragment_size as u64, &mut rtp_payload[pos..]);
if obu_offset == 0 {
rtp_payload[pos] = obu.header & !OBU_SIZE_PRESENT_MASK;
pos += 1;
}
if obu_offset <= 1 && obu.has_extension() {
rtp_payload[pos] = obu.ext_header;
pos += 1;
}
let payload_offset =
obu_offset.saturating_sub(if obu.has_extension() { 2 } else { 1 });
let payload_size = obu.payload.len() - payload_offset;
if !obu.payload.is_empty() && payload_size > 0 {
rtp_payload[pos..pos + payload_size].copy_from_slice(
&obu.payload[payload_offset..payload_offset + payload_size],
);
}
pos += payload_size;
obu_offset = 0;
}
let last_obu = &self.obus[packet.first_obu + packet.num_obu_elements - 1];
let mut obu_fragment_size = packet.last_obu_size;
if packet.num_obu_elements > MAX_NUM_OBUS_TO_OMTI_SIZE {
pos += encode_leb_u63(obu_fragment_size as u64, &mut rtp_payload[pos..]);
}
if obu_offset == 0 && obu_fragment_size > 0 {
rtp_payload[pos] = last_obu.header & !OBU_SIZE_PRESENT_MASK;
pos += 1;
obu_fragment_size -= 1;
}
if obu_offset <= 1 && last_obu.has_extension() && obu_fragment_size > 0 {
rtp_payload[pos] = last_obu.ext_header;
pos += 1;
obu_fragment_size -= 1;
}
let payload_offset =
obu_offset.saturating_sub(if last_obu.has_extension() { 2 } else { 1 });
rtp_payload[pos..pos + obu_fragment_size].copy_from_slice(
&last_obu.payload[payload_offset..payload_offset + obu_fragment_size],
);
payloads.push(rtp_payload);
}
}
fn aggregation_header(&self, packet: &Packet, obus: &[Obu], is_first_packet: bool) -> u8 {
let mut agg_header = 0;
if packet.first_obu_offset > 0 {
agg_header |= 1 << 7;
}
let last_obu_offset = if packet.num_obu_elements == 1 {
packet.first_obu_offset
} else {
0
};
let last_obu_is_fragment = (last_obu_offset + packet.last_obu_size)
< obus[packet.first_obu + packet.num_obu_elements - 1].size;
if last_obu_is_fragment {
agg_header |= 1 << 6;
}
if packet.num_obu_elements <= MAX_NUM_OBUS_TO_OMTI_SIZE {
agg_header |= packet.num_obu_elements << 4;
}
if let Some(ObuType::SequenceHeader) = obus[0].obu_type() {
if is_first_packet {
agg_header |= 1 << 3;
}
}
agg_header as u8
}
fn extra_size_for_previous_obu(&self, packet: &Packet) -> usize {
if packet.packet_size == 0 {
return 0;
}
if packet.num_obu_elements > MAX_NUM_OBUS_TO_OMTI_SIZE {
return 0;
}
leb_128_size(packet.last_obu_size)
}
fn max_fragment_size(&self, size: usize) -> usize {
if size <= 1 {
return 0;
}
let mut idx = 1;
loop {
if size < (1 << 7 * idx) + 1 {
return size - idx;
}
idx += 1;
}
}
}
impl Packetizer for Av1Packetizer {
fn packetize(&mut self, mtu: usize, payload: &[u8]) -> Result<Vec<Vec<u8>>, PacketError> {
if payload.is_empty() || mtu <= AGGREGATION_HEADER_SIZE {
return Ok(vec![]);
}
let mut payloads = vec![];
parse_obus(payload, &mut self.obus)?;
self.emit(mtu, &mut payloads);
Ok(payloads)
}
fn is_marker(&mut self, _data: &[u8], _previous: Option<&[u8]>, last: bool) -> bool {
last
}
}
#[derive(Default, Debug)]
pub struct Av1Depacketizer {
obu_buffer: Vec<u8>,
parsed_obus: Vec<Obu>,
obu_length: usize,
z: bool,
y: bool,
n: bool,
obu_count: u8,
}
impl Av1Depacketizer {
fn parse_aggregation_header(&mut self, agg_header: u8) {
self.z = agg_header & (1 << 7) != 0;
self.y = agg_header & (1 << 6) != 0;
self.obu_count = (agg_header & 0b0011_0000) >> 4;
self.n = agg_header & (1 << 3) != 0;
}
}
impl Depacketizer for Av1Depacketizer {
fn out_size_hint(&self, packets_size: usize) -> Option<usize> {
Some(packets_size)
}
fn depacketize(
&mut self,
packet: &[u8],
out: &mut Vec<u8>,
codec_extra: &mut super::CodecExtra,
) -> Result<(), PacketError> {
if packet.is_empty() {
return Err(PacketError::ErrShortPacket);
}
let mut reader = (packet, 0);
self.parse_aggregation_header(packet[0]);
reader.skip_bytes(1);
if !self.z {
self.obu_length = 0;
self.obu_buffer.clear();
}
let mut is_keyframe = matches!(
*codec_extra,
CodecExtra::Av1(Av1CodecExtra { is_keyframe: true })
);
if self.n {
is_keyframe = true;
self.obu_length = 0;
self.obu_buffer.clear();
}
*codec_extra = CodecExtra::Av1(Av1CodecExtra { is_keyframe });
let mut obu_idx = 0;
while reader.remaining_bits() > 0 {
let is_first_obu = obu_idx == 0;
let mut is_last_obu = self.obu_count != 0 && obu_idx == (self.obu_count - 1);
let fragment_obu_length = if self.obu_count == 0 || !is_last_obu {
let len = reader
.get_leb128()
.ok_or(PacketError::ErrAv1CorruptedPacket)?;
if self.obu_count == 0 && len == reader.remaining_bytes() {
is_last_obu = true;
}
len
} else {
reader.remaining_bytes()
};
if fragment_obu_length == 0 {
return Err(PacketError::ErrAv1CorruptedPacket);
}
if reader.byte_offset() > packet.len()
|| fragment_obu_length > packet.len() - reader.byte_offset()
{
return Err(PacketError::ErrAv1CorruptedPacket);
}
if is_first_obu && self.z {
if self.obu_buffer.is_empty() {
reader.skip_bytes(fragment_obu_length);
obu_idx = 1;
continue;
}
}
let offset = reader.byte_offset();
self.obu_buffer
.extend_from_slice(&packet[offset..offset + fragment_obu_length]);
reader.skip_bytes(fragment_obu_length);
if is_last_obu && self.y {
self.obu_length += fragment_obu_length;
break;
}
self.obu_length += fragment_obu_length;
parse_obus(&self.obu_buffer, &mut self.parsed_obus)?;
let Some(obu) = self.parsed_obus.first_mut() else {
self.obu_length = 0;
self.obu_buffer.clear();
obu_idx += 1;
continue;
};
let size_flag_set = obu.header & OBU_SIZE_PRESENT_MASK != 0;
obu.header |= OBU_SIZE_PRESENT_MASK;
out.push(obu.header);
self.obu_length = self.obu_length.saturating_sub(1);
if obu.has_extension() {
out.push(obu.ext_header);
self.obu_length = self.obu_length.saturating_sub(1);
}
if !size_flag_set {
let mut temp_space = [0u8; 9];
let bytes_written = encode_leb_u63(self.obu_length as u64, &mut temp_space[..]);
out.extend_from_slice(&temp_space[..bytes_written]);
}
if self.obu_length > self.obu_buffer.len() {
return Err(PacketError::ErrAv1CorruptedPacket);
}
let start_idx = self.obu_buffer.len() - self.obu_length;
out.extend_from_slice(&self.obu_buffer[start_idx..start_idx + self.obu_length]);
self.obu_length = 0;
self.obu_buffer.clear();
if is_last_obu {
self.obu_length = 0;
self.obu_buffer.clear();
break;
}
obu_idx += 1;
}
Ok(())
}
fn is_partition_head(&self, packet: &[u8]) -> bool {
if packet.is_empty() {
return false;
}
packet[0] & (1 << 7) == 0
}
fn is_partition_tail(&self, marker: bool, _payload: &[u8]) -> bool {
marker
}
}
#[repr(u8)]
enum ObuType {
SequenceHeader = 1,
TemporalDelimiter = 2,
FrameHeader = 3,
TileGroup = 4,
MetaData = 5,
Frame = 6,
RedundantFrameHeader = 7,
TileList = 8,
Padding = 15,
}
impl ObuType {
fn include_in_packetization(self) -> bool {
!matches!(
self,
Self::TemporalDelimiter | Self::TileList | Self::Padding
)
}
}
#[derive(Default, Debug)]
struct Obu {
header: u8,
ext_header: u8,
payload: Vec<u8>,
size: usize,
}
impl Obu {
fn has_extension(&self) -> bool {
(self.header & OBU_EXTENSION_PRESENT_MASK) != 0
}
fn has_size(&self) -> bool {
(self.header & OBU_SIZE_PRESENT_MASK) != 0
}
fn obu_type(&self) -> Option<ObuType> {
match (self.header & OBU_TYPE_MASK) >> 3 {
1 => Some(ObuType::SequenceHeader),
2 => Some(ObuType::TemporalDelimiter),
3 => Some(ObuType::FrameHeader),
4 => Some(ObuType::TileGroup),
5 => Some(ObuType::MetaData),
6 => Some(ObuType::Frame),
7 => Some(ObuType::RedundantFrameHeader),
8 => Some(ObuType::TileList),
15 => Some(ObuType::Padding),
_ => None,
}
}
}
#[derive(Debug)]
struct Packet {
first_obu: usize,
num_obu_elements: usize,
first_obu_offset: usize,
last_obu_size: usize,
packet_size: usize,
}
impl Packet {
pub fn new(first_obu: usize) -> Self {
Packet {
first_obu,
num_obu_elements: 0,
first_obu_offset: 0,
last_obu_size: 0,
packet_size: 0,
}
}
}
pub fn leb_128_size(mut value: usize) -> usize {
let mut size = 0;
while value >= 0x80 {
size += 1;
value >>= 7;
}
size + 1
}
fn parse_obus(payload: &[u8], parsed_obus: &mut Vec<Obu>) -> Result<(), PacketError> {
let mut reader = (payload, 0);
let mut obu_idx = 0;
while reader.remaining_bits() > 0 {
if obu_idx >= parsed_obus.len() {
parsed_obus.push(Obu::default());
}
let obu = &mut parsed_obus[obu_idx];
obu.header = reader.get_u8().ok_or(PacketError::ErrAv1CorruptedPacket)?;
obu.ext_header = 0;
obu.size = 1;
if obu.has_extension() {
obu.ext_header = reader.get_u8().ok_or(PacketError::ErrAv1CorruptedPacket)?;
obu.size += 1;
}
obu.payload.clear();
if obu.has_size() {
let obu_size = reader
.get_leb128()
.ok_or(PacketError::ErrAv1CorruptedPacket)?;
if obu_size > reader.remaining_bytes() {
return Err(PacketError::ErrAv1CorruptedPacket);
}
let bytes = reader
.get_bytes(obu_size)
.ok_or(PacketError::ErrAv1CorruptedPacket)?;
obu.payload.extend_from_slice(bytes);
} else {
obu.payload.extend_from_slice(reader.get_remaining());
}
obu.size += obu.payload.len();
if let Some(obu_type) = obu.obu_type() {
if obu_type.include_in_packetization() {
obu_idx += 1;
}
}
}
parsed_obus.truncate(obu_idx);
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn packetize_one_frame_type_obu_without_size() {
let payload = &[0x30, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(1200, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
[[0x10, 0x30, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]]
);
}
#[test]
fn packetize_one_frame_type_obu_without_size_with_extension() {
let payload = &[0x34, 0x28, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(1200, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
[[0x10, 0x34, 0x28, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]]
);
}
#[test]
fn packetize_one_frame_type_obu_remove_size_field_without_extension() {
let payload = &[0x32, 0x07, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(1200, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
[[0x10, 0x30, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11]]
);
}
#[test]
fn packetize_one_frame_type_obu_remove_size_field_with_extension() {
let payload = &[0x36, 0x28, 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(1200, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
[[0x10, 0x34, 0x28, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]]
);
}
#[test]
fn omit_size_for_last_obu_when_three_obus_fits_into_the_packet() {
let payload = &[
0x0a, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x2a, 0x04, 0x0b, 0x0c, 0x0d, 0x0e, 0x32, 0x06, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, ];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(1200, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
[[
0x38, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x05, 0x28, 0x0b, 0x0c, 0x0d,
0x0e, 0x30, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a
]]
);
}
#[test]
fn use_size_for_all_obus_when_four_obus_fit_into_the_packet() {
let payload = &[
0x0a, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x2a, 0x04, 0x0b, 0x0c, 0x0d, 0x0e, 0x1a, 0x03, 0x15, 0x16, 0x17, 0x22, 0x06, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, ];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(1200, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
[[
0x08, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x05, 0x28, 0x0b, 0x0c, 0x0d,
0x0e, 0x04, 0x18, 0x15, 0x16, 0x17, 0x07, 0x20, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24
]]
);
}
#[test]
fn discards_temporal_delimiter_and_tile_list_obu() {
let payload = &[
0x12, 0x00, 0x2a, 0x00, 0x42, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x1a, 0x03, 0x15, 0x16, 0x17, 0x22, 0x06, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, ];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(1200, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
[[
0x30, 0x01, 0x28, 0x04, 0x18, 0x15, 0x16, 0x17, 0x20, 0x1f, 0x20, 0x21, 0x22, 0x23,
0x24
]]
);
}
#[test]
fn split_two_obus_into_two_packets_force_split_obu_header() {
let payload = &[
0x1e, 0x28, 0x01, 0x15, 0x26, 0x28, 0x04, 0x0b, 0x0c, 0x0d, 0x0e, ];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(6, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
vec![
vec![0x60, 0x03, 0x1c, 0x28, 0x15, 0x24],
vec![0x90, 0x28, 0x0b, 0x0c, 0x0d, 0x0e]
]
);
}
#[test]
fn set_nbit_at_the_first_packet_of_coded_video_sequence() {}
#[test]
fn split_single_obu_into_two_packets() {
let payload = &[
0x32, 0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(8, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
vec![
vec![0x50, 0x30, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10],
vec![0x90, 0x11, 0x12, 0x13]
]
);
}
#[test]
fn split_single_obu_into_many_packets() {
let mut payload: Vec<u8> = vec![0x32, 0xB0, 0x09]; payload.extend(vec![27u8; 1200]);
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(100, &payload);
assert!(result.is_ok());
assert_eq!(result.unwrap().len(), 13 as usize);
}
#[test]
fn split_two_obus_into_two_packets() {
let payload = &[
0x0a, 0x02, 0x0b, 0x0c, 0x32, 0x09, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, ];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(8, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
vec![
vec![0x68, 0x03, 0x08, 0x0b, 0x0c, 0x30, 0x01, 0x02],
vec![0x90, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]
]
);
}
#[test]
fn split_single_obu_into_two_packets_because_of_mtu_limit() {
let payload = &[
0x32, 0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
];
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(10, payload);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
vec![
vec![0x50, 0x30, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12],
vec![0x90, 0x13]
]
);
}
#[test]
fn obu_payload_size_set_when_absent() {
let paylod = &[0x10, 0x30, 0x14, 0x1e, 0x28];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
let result = depacketizer.depacketize(paylod, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(out, vec![0x32, 0x03, 0x14, 0x1e, 0x28]);
}
#[test]
fn obu_payload_size_set_when_present() {
let paylod = &[0x10, 0x32, 0x03, 0x14, 0x1e, 0x28];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
let result = depacketizer.depacketize(paylod, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(out, vec![0x32, 0x03, 0x14, 0x1e, 0x28]);
}
#[test]
fn obu_payload_size_set_after_extension_when_absent() {
let paylod = &[0x10, 0x34, 0x48, 0x14, 0x1e, 0x28];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
let result = depacketizer.depacketize(paylod, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(out, vec![0x36, 0x48, 0x03, 0x14, 0x1e, 0x28]);
}
#[test]
fn obu_payload_size_set_after_extension_when_present() {
let paylod = &[0x10, 0x36, 0x48, 0x03, 0x14, 0x1e, 0x28];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
let result = depacketizer.depacketize(paylod, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(out, vec![0x36, 0x48, 0x03, 0x14, 0x1e, 0x28]);
}
#[test]
fn one_packet_with_two_obus() {
let paylod = &[0x20, 0x02, 0x08, 0x0a, 0x30, 0x14];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
let result = depacketizer.depacketize(paylod, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(out, vec![0x0a, 0x01, 0x0a, 0x32, 0x01, 0x14]);
}
#[test]
fn one_obu_from_two_packets() {
let payload1 = &[0x50, 0x30, 0x14, 0x1e];
let payload2 = &[0x90, 0x28];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
let result = depacketizer.depacketize(payload1, &mut out, &mut codec_extra);
assert!(result.is_ok());
let result = depacketizer.depacketize(payload2, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(out, vec![0x32, 0x03, 0x14, 0x1e, 0x28]);
}
#[test]
fn two_packets_with_three_obus() {
let payload1 = &[0x60, 0x02, 0x08, 0x0a, 0x30, 0x14, 0x1e];
let payload2 = &[0x90, 0x28];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
let result = depacketizer.depacketize(payload1, &mut out, &mut codec_extra);
assert!(result.is_ok());
let result = depacketizer.depacketize(payload2, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(out, vec![0x0a, 0x01, 0x0a, 0x32, 0x03, 0x14, 0x1e, 0x28]);
}
#[test]
fn two_packets_with_many_obus_some_with_extensions() {
let payload1 = &[
0x40, 0x02, 0x08, 0x0a, 0x02, 0x28, 0x14, 0x04, 0x2c, 0x30, 0x14, 0x1e, 0x05, 0x34,
0x30, 0x28, 0x32, 0x3c,
];
let payload2 = &[0x90, 0x46, 0x50, 0x5a];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
let result = depacketizer.depacketize(payload1, &mut out, &mut codec_extra);
assert!(result.is_ok());
let result = depacketizer.depacketize(payload2, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(
out,
vec![
0x0a, 0x01, 0x0a, 0x2a, 0x01, 0x14, 0x2e, 0x30, 0x02, 0x14, 0x1e, 0x36, 0x30, 0x06,
0x28, 0x32, 0x3c, 0x46, 0x50, 0x5a
]
);
}
#[test]
fn one_obu_from_many_packets() {
let payload1 = &[0x50, 0x30, 0x0b, 0x0c];
let payload2 = &[0xd0, 0x0d, 0x0e];
let payload3 = &[0xd0, 0x0f, 0x10, 0x11];
let payload4 = &[0x90, 0x12];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
let result = depacketizer.depacketize(payload1, &mut out, &mut codec_extra);
assert!(result.is_ok());
let result = depacketizer.depacketize(payload2, &mut out, &mut codec_extra);
assert!(result.is_ok());
let result = depacketizer.depacketize(payload3, &mut out, &mut codec_extra);
assert!(result.is_ok());
let result = depacketizer.depacketize(payload4, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(
out,
vec![0x32, 0x08, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12]
);
}
#[test]
fn many_packets_with_border_aligned_obus() {
let payload1 = &[0x60, 0x03, 0x18, 0x0b, 0x0c, 0x20, 0x15, 0x16, 0x17];
let payload2 = &[0x90, 0x18, 0x19, 0x1a, 0x1b];
let payload3 = &[0x60, 0x03, 0x38, 0x0b, 0x0c, 0x20, 0x1f, 0x20];
let payload4 = &[0x90, 0x21, 0x22, 0x23, 0x24];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
let result = depacketizer.depacketize(payload1, &mut out, &mut codec_extra);
assert!(result.is_ok());
let result = depacketizer.depacketize(payload2, &mut out, &mut codec_extra);
assert!(result.is_ok());
let result = depacketizer.depacketize(payload3, &mut out, &mut codec_extra);
assert!(result.is_ok());
let result = depacketizer.depacketize(payload4, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(
out,
vec![
0x1a, 0x02, 0x0b, 0x0c, 0x22, 0x07, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x3a,
0x02, 0x0b, 0x0c, 0x22, 0x06, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24
]
);
}
#[test]
fn one_packet_one_obu_with_payload_size_127_bytes() {
let mut payload = [0; 131];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
payload[0] = 0b0000_0000; payload[1] = 0x80; payload[2] = 0x01; payload[3] = 0b0011_0000; payload[4 + 42] = 0x42;
let result = depacketizer.depacketize(&payload, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(out[0], 0b0011_0010); assert_eq!(out[1], 127); assert_eq!(out[44], 0x42); }
#[test]
fn two_packets_one_obu_with_payload_size_128_bytes() {
let mut payload1 = [0; 35];
let mut payload2 = [0; 98];
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
payload1[0] = 0b0100_0000; payload1[1] = 33; payload1[2] = 0b0011_0000; payload1[3 + 10] = 0x10;
payload2[0] = 0b1000_0000;
payload2[1] = 96;
payload2[2 + 20] = 0x20;
let result = depacketizer.depacketize(&payload1, &mut out, &mut codec_extra);
assert!(result.is_ok());
let result = depacketizer.depacketize(&payload2, &mut out, &mut codec_extra);
assert!(result.is_ok());
assert_eq!(out[0], 0b0011_0010);
assert_eq!(out[1], 0x80);
assert_eq!(out[2], 0x01);
assert_eq!(out[3 + 10], 0x10);
assert_eq!(out[3 + 32 + 20], 0x20);
}
#[test]
fn leb128_size_boundary_at_128() {
assert_eq!(leb_128_size(0), 1);
assert_eq!(leb_128_size(1), 1);
assert_eq!(leb_128_size(127), 1);
assert_eq!(leb_128_size(128), 2); assert_eq!(leb_128_size(129), 2);
assert_eq!(leb_128_size(255), 2);
}
#[test]
fn leb128_size_boundary_at_16384() {
assert_eq!(leb_128_size(16383), 2);
assert_eq!(leb_128_size(16384), 3); assert_eq!(leb_128_size(16385), 3);
}
#[test]
fn leb128_size_boundary_at_2097152() {
assert_eq!(leb_128_size(2097151), 3);
assert_eq!(leb_128_size(2097152), 4); }
#[test]
fn packetize_obu_with_128_byte_payload_no_panic() {
let mut payload = Vec::with_capacity(3 + 128);
payload.push(0x32); payload.push(0x80); payload.push(0x01); payload.extend(vec![0xAB; 128]);
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(1200, &payload);
assert!(result.is_ok());
let packets = result.unwrap();
assert!(!packets.is_empty());
}
#[test]
fn packetize_128_byte_obu_fragmented_correctly() {
let mut payload = Vec::with_capacity(3 + 128);
payload.push(0x32); payload.push(0x80); payload.push(0x01);
for i in 0u8..128 {
payload.push(i); }
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(50, &payload);
assert!(result.is_ok());
let packets = result.unwrap();
assert!(
packets.len() >= 3,
"128-byte OBU with MTU 50 should produce at least 3 packets"
);
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
for pkt in &packets {
let r = depacketizer.depacketize(pkt, &mut out, &mut codec_extra);
assert!(r.is_ok(), "depacketize failed: {:?}", r);
}
assert!(
out.len() >= 128,
"round-tripped output too short: {} bytes",
out.len()
);
let payload_start = out.len() - 128;
for i in 0u8..128 {
assert_eq!(
out[payload_start + i as usize],
i,
"payload byte {} mismatch after round-trip",
i
);
}
}
#[test]
fn packetize_obu_with_extension_fragmented_round_trip() {
let mut raw = Vec::with_capacity(4 + 200);
raw.push(0x36); raw.push(0x10); raw.push(0xC8); raw.push(0x01);
for i in 0u8..200 {
raw.push(i);
}
let mut packetizer = Av1Packetizer::default();
let result = packetizer.packetize(40, &raw);
assert!(result.is_ok());
let packets = result.unwrap();
assert!(
packets.len() >= 5,
"200-byte OBU with MTU 40 should produce multiple packets"
);
let mut out = Vec::new();
let mut codec_extra = CodecExtra::None;
let mut depacketizer = Av1Depacketizer::default();
for pkt in &packets {
depacketizer
.depacketize(pkt, &mut out, &mut codec_extra)
.unwrap();
}
let payload_start = out.len() - 200;
for i in 0u8..200 {
assert_eq!(
out[payload_start + i as usize],
i,
"extension OBU payload byte {} corrupted after round-trip",
i
);
}
}
#[test]
fn test_detect_av1_keyframe() {
assert!(!detect_av1_keyframe(&[]));
assert!(detect_av1_keyframe(&[0x08]));
assert!(detect_av1_keyframe(&[0x18])); assert!(detect_av1_keyframe(&[0x78])); assert!(detect_av1_keyframe(&[0x88])); assert!(detect_av1_keyframe(&[0x0F]));
assert!(!detect_av1_keyframe(&[0x00]));
assert!(!detect_av1_keyframe(&[0x10])); assert!(!detect_av1_keyframe(&[0x70])); assert!(!detect_av1_keyframe(&[0xF0])); }
}