1use crate::frame::{ContentLightLevel, MasteringDisplay};
37
38#[derive(Debug, Clone, Copy, Default)]
39pub struct HevcHdrSei {
40 pub mastering_display: Option<MasteringDisplay>,
41 pub content_light_level: Option<ContentLightLevel>,
42}
43
44impl HevcHdrSei {
45 pub fn merge(&mut self, other: HevcHdrSei) {
50 if other.mastering_display.is_some() {
51 self.mastering_display = other.mastering_display;
52 }
53 if other.content_light_level.is_some() {
54 self.content_light_level = other.content_light_level;
55 }
56 }
57
58 pub fn is_empty(&self) -> bool {
59 self.mastering_display.is_none() && self.content_light_level.is_none()
60 }
61}
62
63pub fn parse_annexb(buf: &[u8]) -> HevcHdrSei {
68 let mut out = HevcHdrSei::default();
69 for nal in annexb_split(buf) {
70 if nal.is_empty() {
71 continue;
72 }
73 if nal.len() < 2 {
77 continue;
78 }
79 let nal_unit_type = (nal[0] >> 1) & 0x3F;
80 if nal_unit_type != 39 && nal_unit_type != 40 {
81 continue;
82 }
83 let rbsp = strip_emulation_prevention(&nal[2..]);
84 parse_sei_rbsp(&rbsp, &mut out);
85 }
86 out
87}
88
89fn annexb_split(buf: &[u8]) -> Vec<&[u8]> {
93 let mut out = Vec::new();
94 let mut i = 0;
95 while i + 2 < buf.len() {
97 if buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 {
98 i += 3;
99 break;
100 }
101 if i + 3 < buf.len() && buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 0 && buf[i + 3] == 1
102 {
103 i += 4;
104 break;
105 }
106 i += 1;
107 }
108 let mut nal_start = i;
109 while i + 2 < buf.len() {
110 if buf[i] == 0 && buf[i + 1] == 0 && (buf[i + 2] == 1 || buf[i + 2] == 0) {
111 let is_3byte = buf[i + 2] == 1;
113 let is_4byte = !is_3byte && i + 3 < buf.len() && buf[i + 3] == 1;
114 if is_3byte || is_4byte {
115 let mut end = i;
116 while end > nal_start && buf[end - 1] == 0 {
118 end -= 1;
119 }
120 if end > nal_start {
121 out.push(&buf[nal_start..end]);
122 }
123 i += if is_3byte { 3 } else { 4 };
124 nal_start = i;
125 continue;
126 }
127 }
128 i += 1;
129 }
130 if nal_start < buf.len() {
131 let mut end = buf.len();
132 while end > nal_start && buf[end - 1] == 0 {
133 end -= 1;
134 }
135 if end > nal_start {
136 out.push(&buf[nal_start..end]);
137 }
138 }
139 out
140}
141
142fn strip_emulation_prevention(ebsp: &[u8]) -> Vec<u8> {
146 let mut rbsp = Vec::with_capacity(ebsp.len());
147 let mut i = 0;
148 while i < ebsp.len() {
149 if i + 2 < ebsp.len() && ebsp[i] == 0 && ebsp[i + 1] == 0 && ebsp[i + 2] == 0x03 {
150 rbsp.push(0);
151 rbsp.push(0);
152 i += 3;
153 continue;
154 }
155 rbsp.push(ebsp[i]);
156 i += 1;
157 }
158 rbsp
159}
160
161fn parse_sei_rbsp(rbsp: &[u8], out: &mut HevcHdrSei) {
167 let mut cursor = 0;
168 while cursor < rbsp.len() {
169 let (payload_type, after_type) = match read_sei_ff_byte_sum(rbsp, cursor) {
171 Some(v) => v,
172 None => return,
173 };
174 cursor = after_type;
175 if cursor >= rbsp.len() {
176 return;
177 }
178 let (payload_size, after_size) = match read_sei_ff_byte_sum(rbsp, cursor) {
180 Some(v) => v,
181 None => return,
182 };
183 cursor = after_size;
184 if cursor + payload_size > rbsp.len() {
185 return;
186 }
187 let payload = &rbsp[cursor..cursor + payload_size];
188 cursor += payload_size;
189
190 match payload_type {
191 137 => {
192 if let Some(mdcv) = parse_mastering_display(payload) {
193 out.mastering_display = Some(mdcv);
194 }
195 }
196 144 => {
197 if let Some(clli) = parse_content_light_level(payload) {
198 out.content_light_level = Some(clli);
199 }
200 }
201 _ => { }
202 }
203
204 if cursor < rbsp.len() && rbsp[cursor] == 0x80 {
209 break;
210 }
211 }
212}
213
214fn read_sei_ff_byte_sum(buf: &[u8], mut idx: usize) -> Option<(usize, usize)> {
217 let mut acc = 0usize;
218 while idx < buf.len() && buf[idx] == 0xFF {
219 acc += 0xFF;
220 idx += 1;
221 }
222 if idx >= buf.len() {
223 return None;
224 }
225 acc += buf[idx] as usize;
226 Some((acc, idx + 1))
227}
228
229fn parse_mastering_display(p: &[u8]) -> Option<MasteringDisplay> {
242 if p.len() < 24 {
243 return None;
244 }
245 let u16be = |o: usize| u16::from_be_bytes([p[o], p[o + 1]]);
246 let u32be = |o: usize| u32::from_be_bytes([p[o], p[o + 1], p[o + 2], p[o + 3]]);
247 Some(MasteringDisplay {
248 primaries_g_x: u16be(0),
250 primaries_g_y: u16be(2),
251 primaries_b_x: u16be(4),
252 primaries_b_y: u16be(6),
253 primaries_r_x: u16be(8),
254 primaries_r_y: u16be(10),
255 white_point_x: u16be(12),
256 white_point_y: u16be(14),
257 max_luminance: u32be(16),
258 min_luminance: u32be(20),
259 })
260}
261
262fn parse_content_light_level(p: &[u8]) -> Option<ContentLightLevel> {
270 if p.len() < 4 {
271 return None;
272 }
273 Some(ContentLightLevel {
274 max_cll: u16::from_be_bytes([p[0], p[1]]),
275 max_fall: u16::from_be_bytes([p[2], p[3]]),
276 })
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282
283 fn emit_sei_payload(payload_type: u8, payload: &[u8]) -> Vec<u8> {
284 let mut out = Vec::new();
285 out.push(payload_type);
286 out.push(payload.len() as u8);
287 out.extend_from_slice(payload);
288 out.push(0x80);
290 out
291 }
292
293 fn wrap_as_prefix_sei_nal(rbsp: &[u8]) -> Vec<u8> {
294 let mut v = Vec::with_capacity(2 + rbsp.len());
298 v.push(0x4E);
299 v.push(0x01);
300 v.extend_from_slice(rbsp);
301 v
302 }
303
304 fn mastering_display_sei_bytes() -> Vec<u8> {
305 let mut p = Vec::new();
313 p.extend_from_slice(&13250u16.to_be_bytes());
314 p.extend_from_slice(&34500u16.to_be_bytes());
315 p.extend_from_slice(&7500u16.to_be_bytes());
316 p.extend_from_slice(&3000u16.to_be_bytes());
317 p.extend_from_slice(&34000u16.to_be_bytes());
318 p.extend_from_slice(&16000u16.to_be_bytes());
319 p.extend_from_slice(&15635u16.to_be_bytes());
320 p.extend_from_slice(&16450u16.to_be_bytes());
321 p.extend_from_slice(&10_000_000u32.to_be_bytes());
322 p.extend_from_slice(&50u32.to_be_bytes());
323 assert_eq!(p.len(), 24);
324 p
325 }
326
327 fn content_light_level_sei_bytes() -> Vec<u8> {
328 let mut p = Vec::new();
330 p.extend_from_slice(&1000u16.to_be_bytes());
331 p.extend_from_slice(&400u16.to_be_bytes());
332 p
333 }
334
335 fn build_annexb(nals: &[&[u8]]) -> Vec<u8> {
336 let mut out = Vec::new();
337 for nal in nals {
338 out.extend_from_slice(&[0, 0, 0, 1]);
339 out.extend_from_slice(nal);
340 }
341 out
342 }
343
344 #[test]
345 fn parses_mastering_display_sei_from_prefix_nal() {
346 let rbsp = emit_sei_payload(137, &mastering_display_sei_bytes());
347 let nal = wrap_as_prefix_sei_nal(&rbsp);
348 let stream = build_annexb(&[&nal]);
349 let sei = parse_annexb(&stream);
350 let md = sei.mastering_display.expect("mastering display populated");
351 assert_eq!(md.primaries_r_x, 34000);
352 assert_eq!(md.primaries_r_y, 16000);
353 assert_eq!(md.primaries_g_x, 13250);
354 assert_eq!(md.primaries_g_y, 34500);
355 assert_eq!(md.primaries_b_x, 7500);
356 assert_eq!(md.primaries_b_y, 3000);
357 assert_eq!(md.white_point_x, 15635);
358 assert_eq!(md.white_point_y, 16450);
359 assert_eq!(md.max_luminance, 10_000_000);
360 assert_eq!(md.min_luminance, 50);
361 assert!(sei.content_light_level.is_none());
362 }
363
364 #[test]
365 fn parses_content_light_level_sei_from_prefix_nal() {
366 let rbsp = emit_sei_payload(144, &content_light_level_sei_bytes());
367 let nal = wrap_as_prefix_sei_nal(&rbsp);
368 let stream = build_annexb(&[&nal]);
369 let sei = parse_annexb(&stream);
370 let cll = sei.content_light_level.expect("clli populated");
371 assert_eq!(cll.max_cll, 1000);
372 assert_eq!(cll.max_fall, 400);
373 assert!(sei.mastering_display.is_none());
374 }
375
376 #[test]
377 fn parses_both_sei_messages_in_same_nal() {
378 let mut rbsp = emit_sei_payload(137, &mastering_display_sei_bytes());
379 rbsp.pop();
382 rbsp.extend(emit_sei_payload(144, &content_light_level_sei_bytes()));
383 let nal = wrap_as_prefix_sei_nal(&rbsp);
384 let stream = build_annexb(&[&nal]);
385 let sei = parse_annexb(&stream);
386 assert!(sei.mastering_display.is_some());
387 assert!(sei.content_light_level.is_some());
388 }
389
390 #[test]
391 fn handles_emulation_prevention_bytes() {
392 let payload = vec![0x00, 0x00, 0x00, 0x01];
403 let mut rbsp_without_prevention = Vec::new();
404 rbsp_without_prevention.push(144); rbsp_without_prevention.push(payload.len() as u8); rbsp_without_prevention.extend_from_slice(&payload);
407 rbsp_without_prevention.push(0x80); let mut ebsp = Vec::new();
412 let mut zero_run = 0;
413 for &b in &rbsp_without_prevention {
414 if zero_run >= 2 && b <= 0x03 {
415 ebsp.push(0x03);
416 zero_run = 0;
417 }
418 ebsp.push(b);
419 if b == 0 {
420 zero_run += 1;
421 } else {
422 zero_run = 0;
423 }
424 }
425
426 let mut nal = vec![0x4E, 0x01];
428 nal.extend_from_slice(&ebsp);
429 let stream = build_annexb(&[&nal]);
430 let sei = parse_annexb(&stream);
431 let cll = sei.content_light_level.expect("clli after emulation strip");
432 assert_eq!(cll.max_cll, 0);
433 assert_eq!(cll.max_fall, 1);
434 }
435
436 #[test]
437 fn returns_empty_when_no_sei_nal_present() {
438 let mut nal = vec![0x02, 0x01]; nal.extend_from_slice(&[0xFF, 0xFF, 0xFF]);
441 let stream = build_annexb(&[&nal]);
442 let sei = parse_annexb(&stream);
443 assert!(sei.mastering_display.is_none());
444 assert!(sei.content_light_level.is_none());
445 assert!(sei.is_empty());
446 }
447
448 #[test]
449 fn handles_start_code_4byte_variant() {
450 let rbsp = emit_sei_payload(144, &content_light_level_sei_bytes());
451 let nal = wrap_as_prefix_sei_nal(&rbsp);
452 let mut stream = vec![0, 0, 0, 1];
454 stream.extend_from_slice(&nal);
455 let sei = parse_annexb(&stream);
456 assert!(sei.content_light_level.is_some());
457 }
458
459 #[test]
460 fn suffix_sei_nal_type_40_also_parsed() {
461 let rbsp = emit_sei_payload(144, &content_light_level_sei_bytes());
462 let mut nal = vec![0x50, 0x01];
466 nal.extend_from_slice(&rbsp);
467 let stream = build_annexb(&[&nal]);
468 let sei = parse_annexb(&stream);
469 assert!(sei.content_light_level.is_some());
470 }
471
472 #[test]
473 fn ff_byte_sum_handles_large_payload_type() {
474 let mut rbsp = vec![0xFF, 7, 0, 0x80];
477 rbsp.pop();
479 rbsp.extend(emit_sei_payload(144, &content_light_level_sei_bytes()));
480 let nal = wrap_as_prefix_sei_nal(&rbsp);
481 let stream = build_annexb(&[&nal]);
482 let sei = parse_annexb(&stream);
483 assert!(sei.content_light_level.is_some());
484 }
485}