use bytes::{BufMut, Bytes, BytesMut};
use super::h264::ANNEXB_NALUSTART_CODE;
use crate::error::{Error, Result};
use crate::packetizer::{Depacketizer, Payloader};
#[cfg(test)]
mod h265_test;
pub static ANNEXB_3_NALUSTART_CODE: Bytes = Bytes::from_static(&[0x00, 0x00, 0x01]);
pub static SING_PAYLOAD_HDR: Bytes = Bytes::from_static(&[0x1C, 0x01]);
pub static AGGR_PAYLOAD_HDR: Bytes = Bytes::from_static(&[0x60, 0x01]);
pub static FRAG_PAYLOAD_HDR: Bytes = Bytes::from_static(&[0x62, 0x01]);
pub static FU_HDR_IDR_S: u8 = 0x93;
pub static FU_HDR_IDR_M: u8 = 0x13;
pub static FU_HDR_IDR_E: u8 = 0x53;
pub static FU_HDR_P_S: u8 = 0x81;
pub static FU_HDR_P_M: u8 = 0x01;
pub static FU_HDR_P_E: u8 = 0x41;
pub static FU_HDR_B_S: u8 = 0x80;
pub static FU_HDR_B_M: u8 = 0x00;
pub static FU_HDR_B_E: u8 = 0x40;
pub const RTP_OUTBOUND_MTU: usize = 1200;
pub const H265FRAGMENTATION_UNIT_HEADER_SIZE: usize = 1;
pub const NAL_HEADER_SIZE: usize = 2;
#[derive(PartialEq, Hash, Debug, Copy, Clone)]
pub enum UnitType {
VPS = 32,
SPS = 33,
PPS = 34,
CRA = 21,
SEI = 39,
IDR = 19,
PFR = 1,
BFR = 0,
IGNORE = -1,
}
impl UnitType {
pub fn for_id(id: u8) -> Result<UnitType> {
if id > 64 {
Err(Error::ErrUnhandledNaluType)
} else {
let t = match id {
32 => UnitType::VPS,
33 => UnitType::SPS,
34 => UnitType::PPS,
21 => UnitType::CRA,
39 => UnitType::SEI,
19 => UnitType::IDR,
1 => UnitType::PFR,
0 => UnitType::BFR,
_ => UnitType::IGNORE, };
Ok(t)
}
}
}
#[derive(Default, Debug, Clone)]
pub struct HevcPayloader {
vps_nalu: Option<Bytes>,
sps_nalu: Option<Bytes>,
pps_nalu: Option<Bytes>,
}
impl HevcPayloader {
pub fn parse(nalu: &Bytes) -> (Vec<usize>, usize) {
let finder = memchr::memmem::Finder::new(&ANNEXB_NALUSTART_CODE);
let nals = finder.find_iter(nalu).collect::<Vec<usize>>();
if nals.is_empty() {
let finder = memchr::memmem::Finder::new(&ANNEXB_3_NALUSTART_CODE);
return (finder.find_iter(nalu).collect::<Vec<usize>>(), 3);
}
(nals, 4)
}
fn emit(&mut self, nalu: &Bytes, mtu: usize, payloads: &mut Vec<Bytes>) {
if nalu.is_empty() {
return;
}
let payload_header = H265NALUHeader::new(nalu[0], nalu[1]);
let payload_nalu_type = payload_header.nalu_type();
let nalu_type = UnitType::for_id(payload_nalu_type).unwrap_or(UnitType::IGNORE);
if nalu_type == UnitType::IGNORE {
return;
} else if nalu_type == UnitType::VPS {
self.vps_nalu.replace(nalu.clone());
} else if nalu_type == UnitType::SPS {
self.sps_nalu.replace(nalu.clone());
} else if nalu_type == UnitType::PPS {
self.pps_nalu.replace(nalu.clone());
}
if let (Some(vps_nalu), Some(sps_nalu), Some(pps_nalu)) =
(&self.vps_nalu, &self.sps_nalu, &self.pps_nalu)
{
let vps_len = (vps_nalu.len() as u16).to_be_bytes();
let sps_len = (sps_nalu.len() as u16).to_be_bytes();
let pps_len = (pps_nalu.len() as u16).to_be_bytes();
let mut aggr_nalu = BytesMut::new();
aggr_nalu.extend_from_slice(&AGGR_PAYLOAD_HDR);
aggr_nalu.extend_from_slice(&vps_len);
aggr_nalu.extend_from_slice(vps_nalu);
aggr_nalu.extend_from_slice(&sps_len);
aggr_nalu.extend_from_slice(sps_nalu);
aggr_nalu.extend_from_slice(&pps_len);
aggr_nalu.extend_from_slice(pps_nalu);
if aggr_nalu.len() <= mtu {
payloads.push(Bytes::from(aggr_nalu));
self.vps_nalu.take();
self.sps_nalu.take();
self.pps_nalu.take();
return;
}
} else if nalu_type == UnitType::VPS
|| nalu_type == UnitType::SPS
|| nalu_type == UnitType::PPS
{
return;
}
if nalu.len() <= mtu {
payloads.push(nalu.clone());
return;
}
let max_fragment_size =
mtu as isize - NAL_HEADER_SIZE as isize - H265FRAGMENTATION_UNIT_HEADER_SIZE as isize;
let nalu_data = nalu;
let mut nalu_data_index = 2;
let nalu_data_length = nalu.len() as isize - nalu_data_index;
let mut nalu_data_remaining = nalu_data_length;
if std::cmp::min(max_fragment_size, nalu_data_remaining) <= 0 {
return;
}
while nalu_data_remaining > 0 {
let current_fragment_size = std::cmp::min(max_fragment_size, nalu_data_remaining);
let mut out = BytesMut::with_capacity(
H265FRAGMENTATION_UNIT_HEADER_SIZE + current_fragment_size as usize,
);
out.extend_from_slice(&FRAG_PAYLOAD_HDR);
let is_first = nalu_data_index == 2;
let is_last = !is_first && current_fragment_size < max_fragment_size;
if nalu_type == UnitType::IDR {
if is_first {
out.put_u8(FU_HDR_IDR_S);
} else if is_last {
out.put_u8(FU_HDR_IDR_E);
} else {
out.put_u8(FU_HDR_IDR_M);
}
} else if nalu_type == UnitType::PFR {
if is_first {
out.put_u8(FU_HDR_P_S);
} else if is_last {
out.put_u8(FU_HDR_P_E);
} else {
out.put_u8(FU_HDR_P_M);
}
} else if nalu_type == UnitType::BFR {
if is_first {
out.put_u8(FU_HDR_B_S);
} else if is_last {
out.put_u8(FU_HDR_B_E);
} else {
out.put_u8(FU_HDR_B_M);
}
}
out.extend_from_slice(
&nalu_data
[nalu_data_index as usize..(nalu_data_index + current_fragment_size) as usize],
);
payloads.push(out.freeze());
nalu_data_remaining -= current_fragment_size;
nalu_data_index += current_fragment_size;
}
}
}
impl Payloader for HevcPayloader {
fn payload(&mut self, mtu: usize, payload: &Bytes) -> Result<Vec<Bytes>> {
if payload.is_empty() || mtu == 0 {
return Ok(vec![]);
}
let mut payloads = vec![];
let (nal_idxs, offset) = HevcPayloader::parse(payload);
let nal_len = nal_idxs.len();
for (i, start) in nal_idxs.iter().enumerate() {
let end = if (i + 1) < nal_len {
nal_idxs[i + 1]
} else {
payload.len()
};
self.emit(&payload.slice((start + offset)..end), mtu, &mut payloads);
}
Ok(payloads)
}
fn clone_to(&self) -> Box<dyn Payloader + Send + Sync> {
Box::new(self.clone())
}
}
const H265NALU_HEADER_SIZE: usize = 2;
const H265NALU_AGGREGATION_PACKET_TYPE: u8 = 48;
const H265NALU_FRAGMENTATION_UNIT_TYPE: u8 = 49;
const H265NALU_PACI_PACKET_TYPE: u8 = 50;
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub struct H265NALUHeader(pub u16);
impl H265NALUHeader {
pub fn new(high_byte: u8, low_byte: u8) -> Self {
H265NALUHeader(((high_byte as u16) << 8) | low_byte as u16)
}
pub fn f(&self) -> bool {
(self.0 >> 15) != 0
}
pub fn nalu_type(&self) -> u8 {
const MASK: u16 = 0b01111110 << 8;
((self.0 & MASK) >> (8 + 1)) as u8
}
pub fn is_type_vcl_unit(&self) -> bool {
const MSB_MASK: u8 = 0b00100000;
(self.nalu_type() & MSB_MASK) == 0
}
pub fn layer_id(&self) -> u8 {
const MASK: u16 = (0b00000001 << 8) | 0b11111000;
((self.0 & MASK) >> 3) as u8
}
pub fn tid(&self) -> u8 {
const MASK: u16 = 0b00000111;
(self.0 & MASK) as u8
}
pub fn is_aggregation_packet(&self) -> bool {
self.nalu_type() == H265NALU_AGGREGATION_PACKET_TYPE
}
pub fn is_fragmentation_unit(&self) -> bool {
self.nalu_type() == H265NALU_FRAGMENTATION_UNIT_TYPE
}
pub fn is_paci_packet(&self) -> bool {
self.nalu_type() == H265NALU_PACI_PACKET_TYPE
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct H265SingleNALUnitPacket {
payload_header: H265NALUHeader,
donl: Option<u16>,
payload: Bytes,
might_need_donl: bool,
}
impl H265SingleNALUnitPacket {
pub fn with_donl(&mut self, value: bool) {
self.might_need_donl = value;
}
fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
if payload.len() <= H265NALU_HEADER_SIZE {
return Err(Error::ErrShortPacket);
}
let payload_header = H265NALUHeader::new(payload[0], payload[1]);
if payload_header.f() {
return Err(Error::ErrH265CorruptedPacket);
}
if payload_header.is_fragmentation_unit()
|| payload_header.is_paci_packet()
|| payload_header.is_aggregation_packet()
{
return Err(Error::ErrInvalidH265PacketType);
}
let mut payload = payload.slice(2..);
if self.might_need_donl {
if payload.len() <= 2 {
return Err(Error::ErrShortPacket);
}
let donl = ((payload[0] as u16) << 8) | (payload[1] as u16);
self.donl = Some(donl);
payload = payload.slice(2..);
}
self.payload_header = payload_header;
self.payload = payload;
Ok(())
}
pub fn payload_header(&self) -> H265NALUHeader {
self.payload_header
}
pub fn donl(&self) -> Option<u16> {
self.donl
}
pub fn payload(&self) -> Bytes {
self.payload.clone()
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct H265AggregationUnitFirst {
donl: Option<u16>,
nal_unit_size: u16,
nal_unit: Bytes,
}
impl H265AggregationUnitFirst {
pub fn donl(&self) -> Option<u16> {
self.donl
}
pub fn nalu_size(&self) -> u16 {
self.nal_unit_size
}
pub fn nal_unit(&self) -> Bytes {
self.nal_unit.clone()
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct H265AggregationUnit {
dond: Option<u8>,
nal_unit_size: u16,
nal_unit: Bytes,
}
impl H265AggregationUnit {
pub fn dond(&self) -> Option<u8> {
self.dond
}
pub fn nalu_size(&self) -> u16 {
self.nal_unit_size
}
pub fn nal_unit(&self) -> Bytes {
self.nal_unit.clone()
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct H265AggregationPacket {
first_unit: Option<H265AggregationUnitFirst>,
other_units: Vec<H265AggregationUnit>,
might_need_donl: bool,
}
impl H265AggregationPacket {
pub fn with_donl(&mut self, value: bool) {
self.might_need_donl = value;
}
fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
if payload.len() <= H265NALU_HEADER_SIZE {
return Err(Error::ErrShortPacket);
}
let payload_header = H265NALUHeader::new(payload[0], payload[1]);
if payload_header.f() {
return Err(Error::ErrH265CorruptedPacket);
}
if !payload_header.is_aggregation_packet() {
return Err(Error::ErrInvalidH265PacketType);
}
let mut payload = payload.slice(2..);
let mut first_unit = H265AggregationUnitFirst::default();
if self.might_need_donl {
if payload.len() < 2 {
return Err(Error::ErrShortPacket);
}
let donl = ((payload[0] as u16) << 8) | (payload[1] as u16);
first_unit.donl = Some(donl);
payload = payload.slice(2..);
}
if payload.len() < 2 {
return Err(Error::ErrShortPacket);
}
first_unit.nal_unit_size = ((payload[0] as u16) << 8) | (payload[1] as u16);
payload = payload.slice(2..);
if payload.len() < first_unit.nal_unit_size as usize {
return Err(Error::ErrShortPacket);
}
first_unit.nal_unit = payload.slice(..first_unit.nal_unit_size as usize);
payload = payload.slice(first_unit.nal_unit_size as usize..);
let mut units = vec![]; loop {
let mut unit = H265AggregationUnit::default();
if self.might_need_donl {
if payload.is_empty() {
break;
}
let dond = payload[0];
unit.dond = Some(dond);
payload = payload.slice(1..);
}
if payload.len() < 2 {
break;
}
unit.nal_unit_size = ((payload[0] as u16) << 8) | (payload[1] as u16);
payload = payload.slice(2..);
if payload.len() < unit.nal_unit_size as usize {
break;
}
unit.nal_unit = payload.slice(..unit.nal_unit_size as usize);
payload = payload.slice(unit.nal_unit_size as usize..);
units.push(unit);
}
if units.is_empty() {
return Err(Error::ErrShortPacket);
}
self.first_unit = Some(first_unit);
self.other_units = units;
Ok(())
}
pub fn first_unit(&self) -> Option<&H265AggregationUnitFirst> {
self.first_unit.as_ref()
}
pub fn other_units(&self) -> &[H265AggregationUnit] {
self.other_units.as_slice()
}
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub struct H265FragmentationUnitHeader(pub u8);
impl H265FragmentationUnitHeader {
pub fn s(&self) -> bool {
const MASK: u8 = 0b10000000;
((self.0 & MASK) >> 7) != 0
}
pub fn e(&self) -> bool {
const MASK: u8 = 0b01000000;
((self.0 & MASK) >> 6) != 0
}
pub fn fu_type(&self) -> u8 {
const MASK: u8 = 0b00111111;
self.0 & MASK
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct H265FragmentationUnitPacket {
payload_header: H265NALUHeader,
fu_header: H265FragmentationUnitHeader,
donl: Option<u16>,
payload: Bytes,
might_need_donl: bool,
}
impl H265FragmentationUnitPacket {
pub fn with_donl(&mut self, value: bool) {
self.might_need_donl = value;
}
fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
const TOTAL_HEADER_SIZE: usize = H265NALU_HEADER_SIZE + H265FRAGMENTATION_UNIT_HEADER_SIZE;
if payload.len() <= TOTAL_HEADER_SIZE {
return Err(Error::ErrShortPacket);
}
let payload_header = H265NALUHeader::new(payload[0], payload[1]);
if payload_header.f() {
return Err(Error::ErrH265CorruptedPacket);
}
if !payload_header.is_fragmentation_unit() {
return Err(Error::ErrInvalidH265PacketType);
}
let fu_header = H265FragmentationUnitHeader(payload[2]);
let mut payload = payload.slice(3..);
if fu_header.s() && self.might_need_donl {
if payload.len() <= 2 {
return Err(Error::ErrShortPacket);
}
let donl = ((payload[0] as u16) << 8) | (payload[1] as u16);
self.donl = Some(donl);
payload = payload.slice(2..);
}
self.payload_header = payload_header;
self.fu_header = fu_header;
self.payload = payload;
Ok(())
}
pub fn payload_header(&self) -> H265NALUHeader {
self.payload_header
}
pub fn fu_header(&self) -> H265FragmentationUnitHeader {
self.fu_header
}
pub fn donl(&self) -> Option<u16> {
self.donl
}
pub fn payload(&self) -> Bytes {
self.payload.clone()
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct H265PACIPacket {
payload_header: H265NALUHeader,
paci_header_fields: u16,
phes: Bytes,
payload: Bytes,
}
impl H265PACIPacket {
pub fn payload_header(&self) -> H265NALUHeader {
self.payload_header
}
pub fn a(&self) -> bool {
const MASK: u16 = 0b10000000 << 8;
(self.paci_header_fields & MASK) != 0
}
pub fn ctype(&self) -> u8 {
const MASK: u16 = 0b01111110 << 8;
((self.paci_header_fields & MASK) >> (8 + 1)) as u8
}
pub fn phs_size(&self) -> u8 {
const MASK: u16 = (0b00000001 << 8) | 0b11110000;
((self.paci_header_fields & MASK) >> 4) as u8
}
pub fn f0(&self) -> bool {
const MASK: u16 = 0b00001000;
(self.paci_header_fields & MASK) != 0
}
pub fn f1(&self) -> bool {
const MASK: u16 = 0b00000100;
(self.paci_header_fields & MASK) != 0
}
pub fn f2(&self) -> bool {
const MASK: u16 = 0b00000010;
(self.paci_header_fields & MASK) != 0
}
pub fn y(&self) -> bool {
const MASK: u16 = 0b00000001;
(self.paci_header_fields & MASK) != 0
}
pub fn phes(&self) -> Bytes {
self.phes.clone()
}
pub fn payload(&self) -> Bytes {
self.payload.clone()
}
pub fn tsci(&self) -> Option<H265TSCI> {
if !self.f0() || self.phs_size() < 3 {
return None;
}
Some(H265TSCI(
((self.phes[0] as u32) << 16) | ((self.phes[1] as u32) << 8) | self.phes[0] as u32,
))
}
fn depacketize(&mut self, payload: &Bytes) -> Result<()> {
const TOTAL_HEADER_SIZE: usize = H265NALU_HEADER_SIZE + 2;
if payload.len() <= TOTAL_HEADER_SIZE {
return Err(Error::ErrShortPacket);
}
let payload_header = H265NALUHeader::new(payload[0], payload[1]);
if payload_header.f() {
return Err(Error::ErrH265CorruptedPacket);
}
if !payload_header.is_paci_packet() {
return Err(Error::ErrInvalidH265PacketType);
}
let paci_header_fields = ((payload[2] as u16) << 8) | (payload[3] as u16);
let mut payload = payload.slice(4..);
self.paci_header_fields = paci_header_fields;
let header_extension_size = self.phs_size();
if payload.len() < header_extension_size as usize + 1 {
self.paci_header_fields = 0;
return Err(Error::ErrShortPacket);
}
self.payload_header = payload_header;
if header_extension_size > 0 {
self.phes = payload.slice(..header_extension_size as usize);
}
payload = payload.slice(header_extension_size as usize..);
self.payload = payload;
Ok(())
}
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub struct H265TSCI(pub u32);
impl H265TSCI {
pub fn tl0picidx(&self) -> u8 {
const M1: u32 = 0xFFFF0000;
const M2: u32 = 0xFF00;
((((self.0 & M1) >> 16) & M2) >> 8) as u8
}
pub fn irap_pic_id(&self) -> u8 {
const M1: u32 = 0xFFFF0000;
const M2: u32 = 0x00FF;
(((self.0 & M1) >> 16) & M2) as u8
}
pub fn s(&self) -> bool {
const M1: u32 = 0xFF00;
const M2: u32 = 0b10000000;
(((self.0 & M1) >> 8) & M2) != 0
}
pub fn e(&self) -> bool {
const M1: u32 = 0xFF00;
const M2: u32 = 0b01000000;
(((self.0 & M1) >> 8) & M2) != 0
}
pub fn res(&self) -> u8 {
const M1: u32 = 0xFF00;
const M2: u32 = 0b00111111;
(((self.0 & M1) >> 8) & M2) as u8
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum H265Payload {
H265SingleNALUnitPacket(H265SingleNALUnitPacket),
H265FragmentationUnitPacket(H265FragmentationUnitPacket),
H265AggregationPacket(H265AggregationPacket),
H265PACIPacket(H265PACIPacket),
}
impl Default for H265Payload {
fn default() -> Self {
H265Payload::H265SingleNALUnitPacket(H265SingleNALUnitPacket::default())
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct H265Packet {
payload: H265Payload,
might_need_donl: bool,
}
impl H265Packet {
pub fn with_donl(&mut self, value: bool) {
self.might_need_donl = value;
}
pub fn payload(&self) -> &H265Payload {
&self.payload
}
}
impl Depacketizer for H265Packet {
fn depacketize(&mut self, payload: &Bytes) -> Result<Bytes> {
if payload.len() <= H265NALU_HEADER_SIZE {
return Err(Error::ErrShortPacket);
}
let payload_header = H265NALUHeader::new(payload[0], payload[1]);
if payload_header.f() {
return Err(Error::ErrH265CorruptedPacket);
}
if payload_header.is_paci_packet() {
let mut decoded = H265PACIPacket::default();
decoded.depacketize(payload)?;
self.payload = H265Payload::H265PACIPacket(decoded);
} else if payload_header.is_fragmentation_unit() {
let mut decoded = H265FragmentationUnitPacket::default();
decoded.with_donl(self.might_need_donl);
decoded.depacketize(payload)?;
self.payload = H265Payload::H265FragmentationUnitPacket(decoded);
} else if payload_header.is_aggregation_packet() {
let mut decoded = H265AggregationPacket::default();
decoded.with_donl(self.might_need_donl);
decoded.depacketize(payload)?;
self.payload = H265Payload::H265AggregationPacket(decoded);
} else {
let mut decoded = H265SingleNALUnitPacket::default();
decoded.with_donl(self.might_need_donl);
decoded.depacketize(payload)?;
self.payload = H265Payload::H265SingleNALUnitPacket(decoded);
}
Ok(payload.clone())
}
fn is_partition_head(&self, _payload: &Bytes) -> bool {
true
}
fn is_partition_tail(&self, marker: bool, _payload: &Bytes) -> bool {
marker
}
}