[TSM.ID].[11031972] PXE : Platform X Ecosystem I [144 Module] +25 Missing Matrix Modules
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "xcu-codec-h265x"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["TSM.ID <tsm@tsm.id>"]
|
||||
description = "[TSM.ID].[11031972] Custom H.265 NAL Unit Parser"
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1,42 @@
|
||||
#![deny(warnings)]
|
||||
#![allow(dead_code)]
|
||||
//! [TSM.ID].[11031972] -- Platform X Ecosystem
|
||||
//! xcu-codec-h265x -- H.265/HEVC NAL Unit Parser
|
||||
#[derive(Debug)] pub enum H265Error { InvalidNal(String), TruncatedData(String) }
|
||||
impl std::fmt::Display for H265Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::InvalidNal(e)|Self::TruncatedData(e) => write!(f, "{e}") } } }
|
||||
impl std::error::Error for H265Error {}
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum NalType { TrailN=0, TrailR=1, TsaN=2, TsaR=3, StsaN=4, StsaR=5, RadlN=6, RadlR=7, BlaWLp=16, IdrWRadl=19, IdrNLp=20, CraNut=21, VpsPkt=32, SpsPkt=33, PpsPkt=34, Sei=39, Unknown=63 }
|
||||
impl NalType {
|
||||
pub fn from_u8(v: u8) -> Self { match v { 0=>Self::TrailN,1=>Self::TrailR,2=>Self::TsaN,3=>Self::TsaR,4=>Self::StsaN,5=>Self::StsaR,6=>Self::RadlN,7=>Self::RadlR,16=>Self::BlaWLp,19=>Self::IdrWRadl,20=>Self::IdrNLp,21=>Self::CraNut,32=>Self::VpsPkt,33=>Self::SpsPkt,34=>Self::PpsPkt,39=>Self::Sei,_=>Self::Unknown } }
|
||||
pub fn is_keyframe(&self) -> bool { matches!(self, Self::IdrWRadl|Self::IdrNLp|Self::CraNut|Self::BlaWLp) }
|
||||
pub fn is_vcl(&self) -> bool { (*self as u8) < 32 }
|
||||
}
|
||||
#[derive(Debug, Clone)] pub struct NalUnit { pub nal_type: NalType, pub nuh_layer_id: u8, pub nuh_temporal_id: u8, pub payload: Vec<u8> }
|
||||
pub struct H265Parser;
|
||||
impl H265Parser {
|
||||
pub fn parse_nal(data: &[u8]) -> Result<NalUnit, H265Error> {
|
||||
if data.len() < 2 { return Err(H265Error::TruncatedData("Need >= 2 bytes".into())); }
|
||||
let forbidden_zero = (data[0] >> 7) & 1;
|
||||
if forbidden_zero != 0 { return Err(H265Error::InvalidNal("Forbidden bit set".into())); }
|
||||
let nal_type_val = (data[0] >> 1) & 0x3F;
|
||||
let nuh_layer_id = ((data[0] & 1) << 5) | ((data[1] >> 3) & 0x1F);
|
||||
let nuh_temporal_id = (data[1] & 0x07).saturating_sub(1);
|
||||
Ok(NalUnit { nal_type: NalType::from_u8(nal_type_val), nuh_layer_id, nuh_temporal_id, payload: data[2..].to_vec() })
|
||||
}
|
||||
pub fn find_start_codes(data: &[u8]) -> Vec<usize> {
|
||||
let mut positions = Vec::new();
|
||||
let mut i = 0;
|
||||
while i + 2 < data.len() {
|
||||
if data[i] == 0 && data[i+1] == 0 && data[i+2] == 1 { positions.push(i + 3); i += 3; }
|
||||
else if i + 3 < data.len() && data[i] == 0 && data[i+1] == 0 && data[i+2] == 0 && data[i+3] == 1 { positions.push(i + 4); i += 4; }
|
||||
else { i += 1; }
|
||||
}
|
||||
positions
|
||||
}
|
||||
}
|
||||
#[cfg(test)] mod tests {
|
||||
use super::*;
|
||||
#[test] fn test_parse_idr() { let data = [0x28, 0x01, 0xFF]; let nal = H265Parser::parse_nal(&data).unwrap(); assert_eq!(nal.nal_type, NalType::IdrWRadl); assert!(nal.nal_type.is_keyframe()); }
|
||||
#[test] fn test_start_codes() { let data = [0,0,0,1,0x26,0x01,0,0,1,0x28,0x01]; let pos = H265Parser::find_start_codes(&data); assert_eq!(pos.len(), 2); }
|
||||
}
|
||||
Reference in New Issue
Block a user