1mod export;
10mod import;
11mod split;
12
13pub use export::*;
14pub use import::*;
15pub use split::*;
16
17use bytes::{Buf, BufMut, Bytes, BytesMut};
18use scuffle_h265::{NALUnitType, SpsNALUnit};
19
20#[derive(Debug, Clone, thiserror::Error)]
22#[non_exhaustive]
23pub enum Error {
24 #[error("NAL unit is too short")]
25 NalTooShort,
26
27 #[error("{0} too large for hvcC length field ({1} > {max})", max = u16::MAX)]
28 NalTooLargeForHvcc(&'static str, usize),
29
30 #[error("too many {0} for hvcC ({1} > {max})", max = u16::MAX)]
31 TooManyNals(&'static str, usize),
32
33 #[error("NAL too large for 4-byte length prefix")]
34 NalTooLarge,
35
36 #[error("failed to parse SPS NAL unit")]
37 SpsParse,
38
39 #[error("missing level_idc in SPS")]
40 MissingLevelIdc,
41
42 #[error("forbidden zero bit is not zero")]
43 ForbiddenZeroBit,
44
45 #[error("not initialized")]
46 NotInitialized,
47
48 #[error("expected SPS before any frames")]
49 MissingSps,
50
51 #[error("missing timestamp")]
52 MissingTimestamp,
53
54 #[error("HEVCDecoderConfigurationRecord too short")]
55 HvccTooShort,
56
57 #[error("HEVCDecoderConfigurationRecord truncated")]
58 HvccTruncated,
59
60 #[error("hvc1 description for rendition {name:?} is missing VPS, SPS, or PPS (vps={vps}, sps={sps}, pps={pps})")]
61 MissingParamSets {
62 name: String,
63 vps: usize,
64 sps: usize,
65 pps: usize,
66 },
67
68 #[error("annexb: {0}")]
69 Annexb(#[from] crate::codec::annexb::Error),
70}
71
72pub type Result<T> = std::result::Result<T, Error>;
73
74#[derive(Debug, Clone)]
77#[non_exhaustive]
78pub struct Hvcc {
79 pub length_size: usize,
81 pub vps: Vec<Bytes>,
83 pub sps: Vec<Bytes>,
85 pub pps: Vec<Bytes>,
87}
88
89impl Hvcc {
90 pub fn parse(hvcc: &[u8]) -> Result<Self> {
93 if hvcc.len() < 23 {
94 return Err(Error::HvccTooShort);
95 }
96 let length_size = (hvcc[21] & 0x3) as usize + 1;
97 let num_arrays = hvcc[22] as usize;
98
99 let mut vps = Vec::new();
100 let mut sps = Vec::new();
101 let mut pps = Vec::new();
102 let mut pos: usize = 23;
103
104 for _ in 0..num_arrays {
105 let after_hdr = pos.checked_add(3).ok_or(Error::HvccTruncated)?;
106 if hvcc.len() < after_hdr {
107 return Err(Error::HvccTruncated);
108 }
109 let nal_type = hvcc[pos] & 0x3f;
110 let num_nalus = u16::from_be_bytes([hvcc[pos + 1], hvcc[pos + 2]]) as usize;
111 pos = after_hdr;
112
113 for _ in 0..num_nalus {
114 let after_len = pos.checked_add(2).ok_or(Error::HvccTruncated)?;
115 if hvcc.len() < after_len {
116 return Err(Error::HvccTruncated);
117 }
118 let len = u16::from_be_bytes([hvcc[pos], hvcc[pos + 1]]) as usize;
119 let after_nal = after_len.checked_add(len).ok_or(Error::HvccTruncated)?;
120 if hvcc.len() < after_nal {
121 return Err(Error::HvccTruncated);
122 }
123 let bytes = Bytes::copy_from_slice(&hvcc[after_len..after_nal]);
124 pos = after_nal;
125
126 match NALUnitType::from(nal_type) {
127 NALUnitType::VpsNut => vps.push(bytes),
128 NALUnitType::SpsNut => sps.push(bytes),
129 NALUnitType::PpsNut => pps.push(bytes),
130 _ => {}
131 }
132 }
133 }
134
135 Ok(Self {
136 length_size,
137 vps,
138 sps,
139 pps,
140 })
141 }
142}
143
144pub(crate) fn config_from_hvcc(hvcc: &[u8]) -> Result<hang::catalog::VideoConfig> {
153 let params = Hvcc::parse(hvcc)?;
154 let sps_nal = params.sps.first().ok_or(Error::MissingSps)?;
155 let sps = SpsNALUnit::parse(&mut &sps_nal[..]).map_err(|_| Error::SpsParse)?;
156 let profile = &sps.rbsp.profile_tier_level.general_profile;
157
158 let mut config = hang::catalog::VideoConfig::new(hang::catalog::H265 {
159 in_band: false,
160 profile_space: profile.profile_space,
161 profile_idc: profile.profile_idc,
162 profile_compatibility_flags: profile.profile_compatibility_flag.bits().to_be_bytes(),
163 tier_flag: profile.tier_flag,
164 level_idc: profile.level_idc.ok_or(Error::MissingLevelIdc)?,
165 constraint_flags: pack_constraint_flags(profile),
166 });
167 config.coded_width = Some(sps.rbsp.cropped_width() as u32);
168 config.coded_height = Some(sps.rbsp.cropped_height() as u32);
169 config.description = Some(Bytes::copy_from_slice(hvcc));
170 config.container = hang::catalog::Container::Legacy;
171 Ok(config)
172}
173
174pub struct Hvc1 {
181 hvcc: Option<Bytes>,
182 vps: Vec<Bytes>,
184 sps: Vec<Bytes>,
186 pps: Vec<Bytes>,
188}
189
190impl Default for Hvc1 {
191 fn default() -> Self {
192 Self::new()
193 }
194}
195
196impl Hvc1 {
197 pub fn new() -> Self {
199 Self {
200 hvcc: None,
201 vps: Vec::new(),
202 sps: Vec::new(),
203 pps: Vec::new(),
204 }
205 }
206
207 pub fn hvcc(&self) -> Option<&Bytes> {
209 self.hvcc.as_ref()
210 }
211
212 pub fn transform(&mut self, payload: Bytes) -> Result<Option<Bytes>> {
220 let mut buf = payload.clone();
221 let mut nal_iter = crate::codec::annexb::NalIterator::new(&mut buf);
222
223 let mut out = BytesMut::with_capacity(payload.remaining());
224 let mut frame_vps: Vec<Bytes> = Vec::new();
225 let mut frame_sps: Vec<Bytes> = Vec::new();
226 let mut frame_pps: Vec<Bytes> = Vec::new();
227 let mut emitted_any_slice = false;
228
229 loop {
230 let nal = match nal_iter.next() {
231 Some(Ok(n)) => n,
232 Some(Err(e)) => return Err(e.into()),
233 None => break,
234 };
235 if process_nal(&nal, &mut out, &mut frame_vps, &mut frame_sps, &mut frame_pps)? {
236 emitted_any_slice = true;
237 }
238 }
239
240 if let Some(nal) = nal_iter.flush()? {
241 if process_nal(&nal, &mut out, &mut frame_vps, &mut frame_sps, &mut frame_pps)? {
242 emitted_any_slice = true;
243 }
244 }
245
246 let mut changed = false;
251 if !frame_vps.is_empty() && frame_vps != self.vps {
252 self.vps = frame_vps;
253 changed = true;
254 }
255 if !frame_sps.is_empty() && frame_sps != self.sps {
256 self.sps = frame_sps;
257 changed = true;
258 }
259 if !frame_pps.is_empty() && frame_pps != self.pps {
260 self.pps = frame_pps;
261 changed = true;
262 }
263 if changed {
264 self.rebuild_hvcc()?;
265 }
266
267 if !emitted_any_slice {
268 return Ok(None);
269 }
270
271 Ok(Some(out.freeze()))
272 }
273
274 fn rebuild_hvcc(&mut self) -> Result<()> {
275 if self.vps.is_empty() || self.sps.is_empty() || self.pps.is_empty() {
276 return Ok(());
277 }
278 self.hvcc = Some(build_hvcc(&self.vps, &self.sps, &self.pps)?);
279 Ok(())
280 }
281}
282
283fn process_nal(
287 nal: &Bytes,
288 out: &mut BytesMut,
289 frame_vps: &mut Vec<Bytes>,
290 frame_sps: &mut Vec<Bytes>,
291 frame_pps: &mut Vec<Bytes>,
292) -> Result<bool> {
293 if nal.is_empty() {
294 return Ok(false);
295 }
296 match NALUnitType::from((nal[0] >> 1) & 0x3f) {
298 NALUnitType::VpsNut => {
299 crate::codec::annexb::push_distinct(frame_vps, nal);
300 Ok(false)
301 }
302 NALUnitType::SpsNut => {
303 crate::codec::annexb::push_distinct(frame_sps, nal);
304 Ok(false)
305 }
306 NALUnitType::PpsNut => {
307 crate::codec::annexb::push_distinct(frame_pps, nal);
308 Ok(false)
309 }
310 _ => {
311 let len = u32::try_from(nal.len()).map_err(|_| Error::NalTooLarge)?;
312 out.extend_from_slice(&len.to_be_bytes());
313 out.extend_from_slice(nal);
314 Ok(true)
315 }
316 }
317}
318
319pub(crate) fn build_hvcc(vps_nals: &[Bytes], sps_nals: &[Bytes], pps_nals: &[Bytes]) -> Result<Bytes> {
324 let first_sps = sps_nals.first().ok_or(Error::MissingSps)?;
325 for (label, nals) in [("VPS", vps_nals), ("SPS", sps_nals), ("PPS", pps_nals)] {
326 if nals.len() > u16::MAX as usize {
327 return Err(Error::TooManyNals(label, nals.len()));
328 }
329 for nal in nals {
330 if nal.len() > u16::MAX as usize {
331 return Err(Error::NalTooLargeForHvcc(label, nal.len()));
332 }
333 }
334 }
335
336 let sps = SpsNALUnit::parse(&mut &first_sps[..]).map_err(|_| Error::SpsParse)?;
337 let profile = &sps.rbsp.profile_tier_level.general_profile;
338 let level_idc = profile.level_idc.ok_or(Error::MissingLevelIdc)?;
339 let constraint_flags = pack_constraint_flags(profile);
340 let compat = profile.profile_compatibility_flag.bits().to_be_bytes();
341 let num_temporal_layers = sps.rbsp.sps_max_sub_layers_minus1 + 1;
342
343 let params_len: usize = vps_nals
344 .iter()
345 .chain(sps_nals)
346 .chain(pps_nals)
347 .map(|n| 2 + n.len())
348 .sum();
349 let mut out = BytesMut::with_capacity(23 + 3 * 3 + params_len);
350 out.put_u8(1); out.put_u8(((profile.profile_space & 0x3) << 6) | ((profile.tier_flag as u8) << 5) | (profile.profile_idc & 0x1f));
352 out.put_slice(&compat);
353 out.put_slice(&constraint_flags);
354 out.put_u8(level_idc);
355 out.put_u16(0xf000); out.put_u8(0xfc); out.put_u8(0xfc | (sps.rbsp.chroma_format_idc & 0x3));
358 out.put_u8(0xf8 | (sps.rbsp.bit_depth_luma_minus8 & 0x7));
359 out.put_u8(0xf8 | (sps.rbsp.bit_depth_chroma_minus8 & 0x7));
360 out.put_u16(0); out.put_u8(((num_temporal_layers & 0x7) << 3) | ((sps.rbsp.sps_temporal_id_nesting_flag as u8) << 2) | 0x3);
362 out.put_u8(3); for (nal_type, nals) in [
365 (u8::from(NALUnitType::VpsNut), vps_nals),
366 (u8::from(NALUnitType::SpsNut), sps_nals),
367 (u8::from(NALUnitType::PpsNut), pps_nals),
368 ] {
369 out.put_u8(0x80 | (nal_type & 0x3f)); out.put_u16(nals.len() as u16); for nal in nals {
372 out.put_u16(nal.len() as u16);
373 out.put_slice(nal);
374 }
375 }
376
377 Ok(out.freeze())
378}
379
380pub(crate) fn hvcc_params(hvcc: &[u8]) -> anyhow::Result<(usize, Vec<Bytes>)> {
385 anyhow::ensure!(hvcc.len() >= 23, "HEVCDecoderConfigurationRecord too short");
386 let length_size = (hvcc[21] & 0x03) as usize + 1;
387 let num_arrays = hvcc[22];
388
389 let mut params = Vec::new();
390 let mut pos = 23;
391 for _ in 0..num_arrays {
392 anyhow::ensure!(hvcc.len() >= pos + 3, "truncated hvcC NAL array header");
394 pos += 1;
395 let num_nalus = u16::from_be_bytes([hvcc[pos], hvcc[pos + 1]]);
396 pos += 2;
397 for _ in 0..num_nalus {
398 anyhow::ensure!(hvcc.len() >= pos + 2, "truncated hvcC NAL length");
399 let len = u16::from_be_bytes([hvcc[pos], hvcc[pos + 1]]) as usize;
400 pos += 2;
401 anyhow::ensure!(hvcc.len() >= pos + len, "hvcC NAL exceeds buffer");
402 params.push(Bytes::copy_from_slice(&hvcc[pos..pos + len]));
403 pos += len;
404 }
405 }
406
407 Ok((length_size, params))
408}
409
410pub(crate) fn pack_constraint_flags(profile: &scuffle_h265::Profile) -> [u8; 6] {
412 let mut flags = [0u8; 6];
413 flags[0] = ((profile.progressive_source_flag as u8) << 7)
414 | ((profile.interlaced_source_flag as u8) << 6)
415 | ((profile.non_packed_constraint_flag as u8) << 5)
416 | ((profile.frame_only_constraint_flag as u8) << 4);
417 flags
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 #[test]
428 fn hvcc_params_parses_vps_sps_pps() {
429 let vps = &[0x40, 0x01, 0x0c][..]; let sps = &[0x42, 0x01, 0x01, 0x60][..]; let pps = &[0x44, 0x01, 0xc0][..]; let mut hvcc = BytesMut::new();
434 hvcc.extend_from_slice(&[0u8; 21]); hvcc.put_u8(0xfc | 0x03); hvcc.put_u8(3); for (nal_type, nal) in [
438 (u8::from(NALUnitType::VpsNut), vps),
439 (u8::from(NALUnitType::SpsNut), sps),
440 (u8::from(NALUnitType::PpsNut), pps),
441 ] {
442 hvcc.put_u8(0x80 | (nal_type & 0x3f));
443 hvcc.put_u16(1); hvcc.put_u16(nal.len() as u16);
445 hvcc.put_slice(nal);
446 }
447
448 let (length_size, params) = hvcc_params(&hvcc).unwrap();
449 assert_eq!(length_size, 4);
450 assert_eq!(params.len(), 3);
451 assert_eq!(params[0].as_ref(), vps);
452 assert_eq!(params[1].as_ref(), sps);
453 assert_eq!(params[2].as_ref(), pps);
454 }
455}