use std::cmp::min;
use crate::codecs::av1::leb128::leb128_size;
use crate::codecs::av1::obu::{obu_type, Obu, OBU_TYPE_SEQUENCE_HEADER};
pub const MAX_NUM_OBUS_TO_OMIT_SIZE: usize = 3;
pub const AGGREGATION_HEADER_SIZE: usize = 1;
pub struct PacketMetadata {
pub first_obu_index: usize,
pub num_obu_elements: usize,
pub first_obu_offset: usize,
pub last_obu_size: usize,
pub packet_size: usize,
}
impl PacketMetadata {
fn new(first_obu_index: usize) -> Self {
Self {
first_obu_index,
num_obu_elements: 0,
first_obu_offset: 0,
last_obu_size: 0,
packet_size: 0,
}
}
}
pub fn packetize(obus: &[Obu], mtu: usize) -> Vec<PacketMetadata> {
if obus.is_empty() {
return vec![];
}
if mtu < 3 {
return vec![];
}
let mut packets = vec![];
let max_payload_size = mtu - AGGREGATION_HEADER_SIZE;
packets.push(PacketMetadata::new(0));
let mut packet_remaining_bytes = max_payload_size;
for obu_index in 0..obus.len() {
let is_last_obu = obu_index == obus.len() - 1;
let obu = &obus[obu_index];
let mut packet = packets.pop().unwrap();
let mut previous_obu_extra_size = additional_bytes_for_previous_obu_element(&packet);
let min_required_size = if packet.num_obu_elements >= MAX_NUM_OBUS_TO_OMIT_SIZE {
2
} else {
1
};
if packet_remaining_bytes < previous_obu_extra_size + min_required_size {
packets.push(packet);
packet = PacketMetadata::new(obu_index);
packet_remaining_bytes = max_payload_size;
previous_obu_extra_size = 0;
}
packet.packet_size += previous_obu_extra_size;
packet_remaining_bytes -= previous_obu_extra_size;
packet.num_obu_elements += 1;
let must_write_obu_element_size = packet.num_obu_elements > MAX_NUM_OBUS_TO_OMIT_SIZE;
let mut required_bytes = obu.size;
if must_write_obu_element_size {
required_bytes += leb128_size(obu.size as u32);
}
if required_bytes < packet_remaining_bytes {
packet.last_obu_size = obu.size;
packet.packet_size += required_bytes;
packet_remaining_bytes -= required_bytes;
packets.push(packet);
continue;
}
let max_first_fragment_size = if must_write_obu_element_size {
max_fragment_size(packet_remaining_bytes)
} else {
packet_remaining_bytes
};
let first_fragment_size = min(obu.size - 1, max_first_fragment_size);
if first_fragment_size == 0 {
packet.num_obu_elements -= 1;
packet.packet_size -= previous_obu_extra_size;
} else {
packet.packet_size += first_fragment_size;
if must_write_obu_element_size {
packet.packet_size += leb128_size(first_fragment_size as u32);
}
packet.last_obu_size = first_fragment_size;
}
packets.push(packet);
let mut obu_offset = first_fragment_size;
while obu_offset + max_payload_size < obu.size {
let mut packet = PacketMetadata::new(obu_index);
packet.num_obu_elements = 1;
packet.first_obu_offset = obu_offset;
let middle_fragment_size = max_payload_size;
packet.last_obu_size = middle_fragment_size;
packet.packet_size = middle_fragment_size;
packets.push(packet);
obu_offset += max_payload_size;
}
let mut last_fragment_size = obu.size - obu_offset;
if is_last_obu && last_fragment_size > max_payload_size {
let mut semi_last_fragment_size = last_fragment_size / 2;
if semi_last_fragment_size >= last_fragment_size {
semi_last_fragment_size = last_fragment_size - 1;
}
last_fragment_size -= semi_last_fragment_size;
let mut packet = PacketMetadata::new(obu_index);
packet.first_obu_offset = obu_offset;
packet.last_obu_size = semi_last_fragment_size;
packet.packet_size = semi_last_fragment_size;
packets.push(packet);
obu_offset += semi_last_fragment_size
}
let mut last_packet = PacketMetadata::new(obu_index);
last_packet.num_obu_elements = 1;
last_packet.first_obu_offset = obu_offset;
last_packet.last_obu_size = last_fragment_size;
last_packet.packet_size = last_fragment_size;
packets.push(last_packet);
packet_remaining_bytes = max_payload_size - last_fragment_size;
}
packets
}
pub fn get_aggregation_header(obus: &[Obu], packets: &[PacketMetadata], packet_index: usize) -> u8 {
let packet = &packets[packet_index];
let mut header: u8 = 0;
let first_obu_element_is_fragment = packet.first_obu_offset > 0;
if first_obu_element_is_fragment {
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_index + packet.num_obu_elements - 1].size;
if last_obu_is_fragment {
header |= 1 << 6;
}
if packet.num_obu_elements <= MAX_NUM_OBUS_TO_OMIT_SIZE {
header |= (packet.num_obu_elements as u8) << 4;
}
if packet_index == 0 && obu_type(obus.first().unwrap().header) == OBU_TYPE_SEQUENCE_HEADER {
header |= 1 << 3;
}
header
}
fn additional_bytes_for_previous_obu_element(packet: &PacketMetadata) -> usize {
if packet.packet_size == 0 || packet.num_obu_elements > MAX_NUM_OBUS_TO_OMIT_SIZE {
0
} else {
leb128_size(packet.last_obu_size as u32)
}
}
fn max_fragment_size(remaining_bytes: usize) -> usize {
if remaining_bytes <= 1 {
return 0;
}
let mut i = 1;
loop {
if remaining_bytes < (1 << (7 * i)) + i {
return remaining_bytes - i;
}
i += 1;
}
}