nandi/jolt-nativepublic Fork 0
2ab40bf193b6a39bc8d1397cc36d17572bbf59cd
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

avcc.rs · 154 lines · 4.4 KBRust Blame HistoryRaw
Bring vidya in cfd3e36 nandi 19d ago1//! AVCC (length-prefixed) → Annex B converter for H.264-in-MP4.
2//!
3//! Adapted from the `openh264` crate's `examples/mp4` helper (BSD-2-Clause).
4
5use mp4::Mp4Track;
6
7/// Network abstraction layer type for an H.264 NAL unit.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9enum NalType {
10 Unspecified = 0,
11 Slice = 1,
12 IdrSlice = 5,
13 Sps = 7,
14 Pps = 8,
15 Other,
16}
17
18impl From<u8> for NalType {
19 fn from(value: u8) -> Self {
20 match value & 0x1F {
21 0 => Self::Unspecified,
22 1 => Self::Slice,
23 5 => Self::IdrSlice,
24 7 => Self::Sps,
25 8 => Self::Pps,
26 _ => Self::Other,
27 }
28 }
29}
30
31struct NalUnit<'a> {
32 nal_type: NalType,
33 bytes: &'a [u8],
34}
35
36impl<'a> NalUnit<'a> {
37 fn from_stream(mut stream: &'a [u8], length_size: u8) -> Option<(Self, &'a [u8])> {
38 if stream.len() < length_size as usize {
39 return None;
40 }
41 let mut nal_size = 0u32;
42 for _ in 0..length_size {
43 nal_size = (nal_size << 8) | u32::from(stream[0]);
44 stream = &stream[1..];
45 }
46 if nal_size == 0 || stream.len() < nal_size as usize {
47 return None;
48 }
49 let packet = &stream[..nal_size as usize];
50 let nal_type = NalType::from(packet[0]);
51 let unit = NalUnit {
52 nal_type,
53 bytes: packet,
54 };
55 stream = &stream[nal_size as usize..];
56 Some((unit, stream))
57 }
58}
59
60/// Convert MP4/AVCC length-prefixed NAL units to Annex B for OpenH264.
61///
62/// Also injects SPS/PPS from the `avcC` box when an IDR arrives without them.
63pub struct Mp4BitstreamConverter {
64 length_size: u8,
65 sps: Vec<Vec<u8>>,
66 pps: Vec<Vec<u8>>,
67 new_idr: bool,
68 sps_seen: bool,
69 pps_seen: bool,
70}
71
72impl Mp4BitstreamConverter {
73 pub fn for_mp4_track(track: &Mp4Track) -> Result<Self, String> {
74 let avcc_config = &track
75 .trak
76 .mdia
77 .minf
78 .stbl
79 .stsd
80 .avc1
81 .as_ref()
82 .ok_or_else(|| "Track does not contain AVC1/avcC config".to_string())?
83 .avcc;
84
85 Ok(Self {
86 length_size: avcc_config.length_size_minus_one + 1,
87 sps: avcc_config
88 .sequence_parameter_sets
89 .iter()
90 .cloned()
91 .map(|v| v.bytes)
92 .collect(),
93 pps: avcc_config
94 .picture_parameter_sets
95 .iter()
96 .cloned()
97 .map(|v| v.bytes)
98 .collect(),
99 new_idr: true,
100 sps_seen: false,
101 pps_seen: false,
102 })
103 }
104
105 pub fn convert_packet(&mut self, packet: &[u8], out: &mut Vec<u8>) {
106 let mut stream = packet;
107 out.clear();
108
109 while !stream.is_empty() {
110 let Some((unit, remaining_stream)) = NalUnit::from_stream(stream, self.length_size)
111 else {
112 break;
113 };
114 stream = remaining_stream;
115
116 match unit.nal_type {
117 NalType::Sps => self.sps_seen = true,
118 NalType::Pps => self.pps_seen = true,
119 NalType::IdrSlice => {
120 if !self.new_idr && unit.bytes.len() > 1 && unit.bytes[1] & 0x80 != 0 {
121 self.new_idr = true;
122 }
123 if self.new_idr && !self.sps_seen && !self.pps_seen {
124 self.new_idr = false;
125 for sps in &self.sps {
126 out.extend([0, 0, 1]);
127 out.extend(sps);
128 }
129 for pps in &self.pps {
130 out.extend([0, 0, 1]);
131 out.extend(pps);
132 }
133 }
134 if self.new_idr && self.sps_seen && !self.pps_seen {
135 for pps in &self.pps {
136 out.extend([0, 0, 1]);
137 out.extend(pps);
138 }
139 }
140 }
141 _ => {}
142 }
143
144 out.extend([0, 0, 1]);
145 out.extend(unit.bytes);
146
147 if !self.new_idr && unit.nal_type == NalType::Slice {
148 self.new_idr = true;
149 self.sps_seen = false;
150 self.pps_seen = false;
151 }
152 }
153 }
154}