#[cfg(test)]
mod h26x_writer_test;
use std::io::Write;
use bytes::{Bytes, BytesMut};
use rtp::codec::h264::H264Packet;
use rtp::codec::h265::{H265Packet, H265Payload};
use rtp::packetizer::Depacketizer;
use crate::io::Writer;
use shared::error::Result;
const NALU_TTYPE_STAP_A: u32 = 24;
const NALU_TTYPE_SPS: u32 = 7;
const NALU_TYPE_BITMASK: u32 = 0x1F;
const ANNEXB_NALUSTART_CODE: &[u8] = &[0x00, 0x00, 0x00, 0x01];
fn rebuild_h265_nalu_header(
payload_header: rtp::codec::h265::H265NALUHeader,
fu_type: u8,
) -> [u8; 2] {
let mut raw = (fu_type as u16) << 9;
raw |= (payload_header.layer_id() as u16) << 3;
raw |= payload_header.tid() as u16;
if payload_header.f() {
raw |= 1 << 15;
}
raw.to_be_bytes()
}
fn is_key_frame(data: &[u8]) -> bool {
if data.len() < 4 {
false
} else {
let word = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
let nalu_type = (word >> 24) & NALU_TYPE_BITMASK;
(nalu_type == NALU_TTYPE_STAP_A && (word & NALU_TYPE_BITMASK) == NALU_TTYPE_SPS)
|| (nalu_type == NALU_TTYPE_SPS)
}
}
enum H26xPacket {
H264(H264Packet),
H265(H265Packet),
}
impl H26xPacket {
fn depacketize(&mut self, payload: &Bytes) -> Result<Bytes> {
match self {
H26xPacket::H264(p) => p.depacketize(payload),
H26xPacket::H265(p) => p.depacketize(payload),
}
}
}
pub struct H26xWriter<W: Write> {
writer: W,
packet: H26xPacket,
is_hevc: bool,
has_key_frame: bool,
buffer: BytesMut,
}
impl<W: Write> H26xWriter<W> {
pub fn new(writer: W, is_hevc: bool) -> Self {
H26xWriter {
writer,
packet: if is_hevc {
H26xPacket::H265(H265Packet::default())
} else {
H26xPacket::H264(H264Packet::default())
},
is_hevc,
has_key_frame: false,
buffer: BytesMut::new(),
}
}
}
impl<W: Write> Writer for H26xWriter<W> {
fn write_rtp(&mut self, packet: &rtp::Packet) -> Result<()> {
if packet.payload.is_empty() {
return Ok(());
}
if self.is_hevc {
self.write_h265(packet)
} else {
self.write_h264(packet)
}
}
fn close(&mut self) -> Result<()> {
if !self.buffer.is_empty() {
self.writer.write_all(&self.buffer)?;
self.buffer.clear();
}
self.writer.flush()?;
Ok(())
}
}
impl<W: Write> H26xWriter<W> {
fn write_h264(&mut self, packet: &rtp::Packet) -> Result<()> {
if !self.has_key_frame {
self.has_key_frame = is_key_frame(&packet.payload);
if !self.has_key_frame {
return Ok(());
}
}
let payload = self
.packet
.depacketize(&Bytes::copy_from_slice(&packet.payload))?;
self.writer.write_all(&payload)?;
Ok(())
}
fn write_h265(&mut self, packet: &rtp::Packet) -> Result<()> {
let _data = self
.packet
.depacketize(&Bytes::copy_from_slice(&packet.payload))?;
if let H26xPacket::H265(h265_packet) = &self.packet {
match h265_packet.payload() {
H265Payload::H265PACIPacket(_p) => {
}
H265Payload::H265SingleNALUnitPacket(p) => {
self.writer.write_all(ANNEXB_NALUSTART_CODE)?;
self.writer.write_all(&p.payload_header().0.to_be_bytes())?;
self.writer.write_all(&p.payload())?;
}
H265Payload::H265AggregationPacket(p) => {
if let Some(uf) = p.first_unit() {
self.writer.write_all(ANNEXB_NALUSTART_CODE)?;
self.writer.write_all(&uf.nal_unit())?;
}
for ou in p.other_units() {
self.writer.write_all(ANNEXB_NALUSTART_CODE)?;
self.writer.write_all(&ou.nal_unit())?;
}
}
H265Payload::H265FragmentationUnitPacket(p) => {
if p.fu_header().s() {
self.buffer.clear();
self.buffer.extend_from_slice(ANNEXB_NALUSTART_CODE);
self.buffer.extend_from_slice(&rebuild_h265_nalu_header(
p.payload_header(),
p.fu_header().fu_type(),
));
}
self.buffer.extend_from_slice(&p.payload());
if p.fu_header().e() {
self.writer.write_all(&self.buffer)?;
self.buffer.clear();
}
}
}
}
Ok(())
}
}