1use crate::error::{CodecError, CodecResult};
21
22pub const AV1_OBU_TYPE_METADATA: u8 = 5;
26
27pub const VP8_USER_DATA_MARKER: u8 = 0xFE;
29
30pub const UUID_LEN: usize = 16;
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43#[repr(u8)]
44pub enum SeiPayloadType {
45 BufferingPeriod = 0,
47 PictureTiming = 1,
49 PanScanRect = 2,
51 UserDataRegistered = 4,
53 UserDataUnregistered = 5,
55 RecoveryPoint = 6,
57 FramePacking = 45,
59 DisplayOrientation = 47,
61 Unknown = 255,
63}
64
65impl SeiPayloadType {
66 pub fn from_byte(b: u8) -> Self {
68 match b {
69 0 => Self::BufferingPeriod,
70 1 => Self::PictureTiming,
71 2 => Self::PanScanRect,
72 4 => Self::UserDataRegistered,
73 5 => Self::UserDataUnregistered,
74 6 => Self::RecoveryPoint,
75 45 => Self::FramePacking,
76 47 => Self::DisplayOrientation,
77 _ => Self::Unknown,
78 }
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct UserDataUnregistered {
89 pub uuid: [u8; UUID_LEN],
91 pub data: Vec<u8>,
93}
94
95impl UserDataUnregistered {
96 pub fn new(uuid: [u8; UUID_LEN], data: Vec<u8>) -> Self {
98 Self { uuid, data }
99 }
100
101 pub fn with_nil_uuid(data: Vec<u8>) -> Self {
103 Self::new([0u8; UUID_LEN], data)
104 }
105
106 pub fn to_bytes(&self) -> Vec<u8> {
108 let mut out = Vec::with_capacity(UUID_LEN + self.data.len());
109 out.extend_from_slice(&self.uuid);
110 out.extend_from_slice(&self.data);
111 out
112 }
113
114 pub fn from_bytes(raw: &[u8]) -> CodecResult<Self> {
116 if raw.len() < UUID_LEN {
117 return Err(CodecError::InvalidBitstream(format!(
118 "UserDataUnregistered: need {UUID_LEN} bytes for UUID, got {}",
119 raw.len()
120 )));
121 }
122 let mut uuid = [0u8; UUID_LEN];
123 uuid.copy_from_slice(&raw[..UUID_LEN]);
124 Ok(Self {
125 uuid,
126 data: raw[UUID_LEN..].to_vec(),
127 })
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Eq)]
139pub struct PictureTiming {
140 pub clock_timestamp_flag: bool,
142 pub clock_timestamp: u64,
144 pub presentation_delay: u32,
146 pub pic_struct: PicStructure,
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
152#[repr(u8)]
153pub enum PicStructure {
154 Frame = 0,
156 TopField = 1,
158 BottomField = 2,
160 TopBottomField = 3,
162 BottomTopField = 4,
164}
165
166impl PicStructure {
167 fn from_byte(b: u8) -> Self {
168 match b {
169 1 => Self::TopField,
170 2 => Self::BottomField,
171 3 => Self::TopBottomField,
172 4 => Self::BottomTopField,
173 _ => Self::Frame,
174 }
175 }
176}
177
178impl PictureTiming {
179 pub fn frame(clock_timestamp: u64, presentation_delay: u32) -> Self {
181 Self {
182 clock_timestamp_flag: true,
183 clock_timestamp,
184 presentation_delay,
185 pic_struct: PicStructure::Frame,
186 }
187 }
188
189 pub fn to_bytes(&self) -> Vec<u8> {
191 let mut out = Vec::with_capacity(16);
192 out.push(u8::from(self.clock_timestamp_flag));
193 out.extend_from_slice(&self.clock_timestamp.to_be_bytes());
194 out.extend_from_slice(&self.presentation_delay.to_be_bytes());
195 out.push(self.pic_struct as u8);
196 out.extend_from_slice(&[0u8; 2]); out
198 }
199
200 pub fn from_bytes(raw: &[u8]) -> CodecResult<Self> {
202 if raw.len() < 14 {
203 return Err(CodecError::InvalidBitstream(format!(
204 "PictureTiming: need 14 bytes, got {}",
205 raw.len()
206 )));
207 }
208 let clock_timestamp_flag = raw[0] != 0;
209 let clock_timestamp =
210 u64::from_be_bytes(raw[1..9].try_into().map_err(|_| {
211 CodecError::InvalidBitstream("PictureTiming: bad clock bytes".into())
212 })?);
213 let presentation_delay =
214 u32::from_be_bytes(raw[9..13].try_into().map_err(|_| {
215 CodecError::InvalidBitstream("PictureTiming: bad delay bytes".into())
216 })?);
217 let pic_struct = PicStructure::from_byte(raw[13]);
218 Ok(Self {
219 clock_timestamp_flag,
220 clock_timestamp,
221 presentation_delay,
222 pic_struct,
223 })
224 }
225}
226
227#[derive(Debug, Clone, PartialEq, Eq)]
231pub struct SeiMessage {
232 pub payload_type: SeiPayloadType,
234 pub payload: Vec<u8>,
236}
237
238impl SeiMessage {
239 pub fn new(payload_type: SeiPayloadType, payload: Vec<u8>) -> Self {
241 Self {
242 payload_type,
243 payload,
244 }
245 }
246
247 pub fn user_data_unregistered(udu: &UserDataUnregistered) -> Self {
249 Self::new(SeiPayloadType::UserDataUnregistered, udu.to_bytes())
250 }
251
252 pub fn picture_timing(pt: &PictureTiming) -> Self {
254 Self::new(SeiPayloadType::PictureTiming, pt.to_bytes())
255 }
256
257 pub fn as_user_data_unregistered(&self) -> CodecResult<UserDataUnregistered> {
259 if self.payload_type != SeiPayloadType::UserDataUnregistered {
260 return Err(CodecError::InvalidData(
261 "SEI: expected UserDataUnregistered payload type".into(),
262 ));
263 }
264 UserDataUnregistered::from_bytes(&self.payload)
265 }
266
267 pub fn as_picture_timing(&self) -> CodecResult<PictureTiming> {
269 if self.payload_type != SeiPayloadType::PictureTiming {
270 return Err(CodecError::InvalidData(
271 "SEI: expected PictureTiming payload type".into(),
272 ));
273 }
274 PictureTiming::from_bytes(&self.payload)
275 }
276}
277
278#[derive(Debug, Default)]
287pub struct SeiEncoder {
288 buf: Vec<u8>,
289}
290
291impl SeiEncoder {
292 pub fn new() -> Self {
294 Self::default()
295 }
296
297 pub fn write_message(&mut self, msg: &SeiMessage) {
299 self.buf.push(msg.payload_type as u8);
300 let len = msg.payload.len() as u32;
301 self.buf.extend_from_slice(&len.to_be_bytes());
302 self.buf.extend_from_slice(&msg.payload);
303 }
304
305 pub fn write_messages(&mut self, msgs: &[SeiMessage]) {
307 for msg in msgs {
308 self.write_message(msg);
309 }
310 }
311
312 pub fn finish(self) -> Vec<u8> {
314 self.buf
315 }
316
317 pub fn len(&self) -> usize {
319 self.buf.len()
320 }
321
322 pub fn is_empty(&self) -> bool {
324 self.buf.is_empty()
325 }
326}
327
328#[derive(Debug)]
330pub struct SeiDecoder<'a> {
331 data: &'a [u8],
332 pos: usize,
333}
334
335impl<'a> SeiDecoder<'a> {
336 pub fn new(data: &'a [u8]) -> Self {
338 Self { data, pos: 0 }
339 }
340
341 pub fn next_message(&mut self) -> CodecResult<Option<SeiMessage>> {
343 if self.pos >= self.data.len() {
344 return Ok(None);
345 }
346 let type_byte = self.data[self.pos];
348 self.pos += 1;
349
350 if self.pos + 4 > self.data.len() {
352 return Err(CodecError::InvalidBitstream(
353 "SEI: truncated length field".into(),
354 ));
355 }
356 let length = u32::from_be_bytes(
357 self.data[self.pos..self.pos + 4]
358 .try_into()
359 .map_err(|_| CodecError::InvalidBitstream("SEI: bad length bytes".into()))?,
360 ) as usize;
361 self.pos += 4;
362
363 if self.pos + length > self.data.len() {
365 return Err(CodecError::InvalidBitstream(format!(
366 "SEI: payload truncated (need {length}, have {})",
367 self.data.len() - self.pos
368 )));
369 }
370 let payload = self.data[self.pos..self.pos + length].to_vec();
371 self.pos += length;
372
373 Ok(Some(SeiMessage {
374 payload_type: SeiPayloadType::from_byte(type_byte),
375 payload,
376 }))
377 }
378
379 pub fn collect_all(&mut self) -> CodecResult<Vec<SeiMessage>> {
381 let mut result = Vec::new();
382 while let Some(msg) = self.next_message()? {
383 result.push(msg);
384 }
385 Ok(result)
386 }
387}
388
389#[derive(Debug, Clone, PartialEq, Eq)]
397pub struct Av1MetadataObu {
398 pub metadata_type: u8,
400 pub extension_flag: bool,
402 pub payload: Vec<u8>,
404}
405
406impl Av1MetadataObu {
407 pub fn new(metadata_type: u8, payload: Vec<u8>) -> Self {
409 Self {
410 metadata_type,
411 extension_flag: false,
412 payload,
413 }
414 }
415
416 pub fn to_bytes(&self) -> Vec<u8> {
423 let extension_bit = u8::from(self.extension_flag) << 2;
425 let obu_header = (AV1_OBU_TYPE_METADATA << 3) | extension_bit;
426
427 let mut out = Vec::with_capacity(2 + self.payload.len());
428 out.push(obu_header);
429 if self.extension_flag {
430 out.push(0x00); }
432 out.push(self.metadata_type);
433 out.extend_from_slice(&self.payload);
434 out
435 }
436
437 pub fn from_bytes(raw: &[u8]) -> CodecResult<Self> {
439 if raw.is_empty() {
440 return Err(CodecError::InvalidBitstream(
441 "Av1MetadataObu: empty input".into(),
442 ));
443 }
444 let obu_header = raw[0];
445 let obu_type = (obu_header >> 3) & 0x0F;
446 if obu_type != AV1_OBU_TYPE_METADATA {
447 return Err(CodecError::InvalidBitstream(format!(
448 "Av1MetadataObu: expected type {AV1_OBU_TYPE_METADATA}, got {obu_type}"
449 )));
450 }
451 let extension_flag = (obu_header >> 2) & 1 != 0;
452 let mut pos = 1usize;
453 if extension_flag {
454 pos += 1; }
456 if pos >= raw.len() {
457 return Err(CodecError::InvalidBitstream(
458 "Av1MetadataObu: missing metadata_type".into(),
459 ));
460 }
461 let metadata_type = raw[pos];
462 pos += 1;
463 let payload = raw[pos..].to_vec();
464 Ok(Self {
465 metadata_type,
466 extension_flag,
467 payload,
468 })
469 }
470}
471
472#[derive(Debug, Clone, PartialEq, Eq)]
485pub struct Vp8MetadataBlock {
486 pub payload: Vec<u8>,
488}
489
490impl Vp8MetadataBlock {
491 pub fn new(payload: Vec<u8>) -> Self {
493 Self { payload }
494 }
495
496 pub fn to_bytes(&self) -> Vec<u8> {
500 let mut out = Vec::with_capacity(5 + self.payload.len());
501 out.push(VP8_USER_DATA_MARKER);
502 let len = self.payload.len() as u32;
503 out.extend_from_slice(&len.to_be_bytes());
504 out.extend_from_slice(&self.payload);
505 out
506 }
507
508 pub fn from_bytes(raw: &[u8]) -> CodecResult<Self> {
510 if raw.is_empty() || raw[0] != VP8_USER_DATA_MARKER {
511 return Err(CodecError::InvalidBitstream(
512 "Vp8MetadataBlock: missing marker byte".into(),
513 ));
514 }
515 if raw.len() < 5 {
516 return Err(CodecError::InvalidBitstream(
517 "Vp8MetadataBlock: truncated header".into(),
518 ));
519 }
520 let length = u32::from_be_bytes(
521 raw[1..5]
522 .try_into()
523 .map_err(|_| CodecError::InvalidBitstream("Vp8MetadataBlock: bad length".into()))?,
524 ) as usize;
525 if raw.len() < 5 + length {
526 return Err(CodecError::InvalidBitstream(format!(
527 "Vp8MetadataBlock: payload truncated (need {length}, have {})",
528 raw.len() - 5
529 )));
530 }
531 Ok(Self {
532 payload: raw[5..5 + length].to_vec(),
533 })
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540
541 #[test]
542 fn test_user_data_unregistered_roundtrip() {
543 let uuid = [0x01u8; UUID_LEN];
544 let data = b"hello sei world".to_vec();
545 let udu = UserDataUnregistered::new(uuid, data.clone());
546 let raw = udu.to_bytes();
547 let parsed = UserDataUnregistered::from_bytes(&raw).unwrap();
548 assert_eq!(parsed.uuid, uuid);
549 assert_eq!(parsed.data, data);
550 }
551
552 #[test]
553 fn test_user_data_unregistered_too_short() {
554 let result = UserDataUnregistered::from_bytes(&[0u8; 10]);
555 assert!(result.is_err());
556 }
557
558 #[test]
559 fn test_picture_timing_roundtrip() {
560 let pt = PictureTiming::frame(123_456_789, 3000);
561 let raw = pt.to_bytes();
562 let parsed = PictureTiming::from_bytes(&raw).unwrap();
563 assert_eq!(parsed.clock_timestamp, 123_456_789);
564 assert_eq!(parsed.presentation_delay, 3000);
565 assert_eq!(parsed.pic_struct, PicStructure::Frame);
566 assert!(parsed.clock_timestamp_flag);
567 }
568
569 #[test]
570 fn test_picture_timing_too_short() {
571 let result = PictureTiming::from_bytes(&[0u8; 5]);
572 assert!(result.is_err());
573 }
574
575 #[test]
576 fn test_sei_encoder_decoder_roundtrip() {
577 let udu = UserDataUnregistered::with_nil_uuid(b"test payload".to_vec());
578 let pt = PictureTiming::frame(999, 500);
579
580 let mut enc = SeiEncoder::new();
581 enc.write_message(&SeiMessage::user_data_unregistered(&udu));
582 enc.write_message(&SeiMessage::picture_timing(&pt));
583 let bytes = enc.finish();
584
585 let mut dec = SeiDecoder::new(&bytes);
586 let messages = dec.collect_all().unwrap();
587 assert_eq!(messages.len(), 2);
588
589 assert_eq!(
590 messages[0].payload_type,
591 SeiPayloadType::UserDataUnregistered
592 );
593 let recovered_udu = messages[0].as_user_data_unregistered().unwrap();
594 assert_eq!(recovered_udu.data, b"test payload");
595
596 assert_eq!(messages[1].payload_type, SeiPayloadType::PictureTiming);
597 let recovered_pt = messages[1].as_picture_timing().unwrap();
598 assert_eq!(recovered_pt.clock_timestamp, 999);
599 }
600
601 #[test]
602 fn test_sei_decoder_truncated_length() {
603 let bad = &[SeiPayloadType::PictureTiming as u8];
605 let mut dec = SeiDecoder::new(bad);
606 assert!(dec.next_message().is_err());
607 }
608
609 #[test]
610 fn test_sei_decoder_truncated_payload() {
611 let mut enc = SeiEncoder::new();
612 enc.write_message(&SeiMessage::new(
613 SeiPayloadType::UserDataUnregistered,
614 vec![0u8; 20],
615 ));
616 let mut bytes = enc.finish();
617 bytes.truncate(bytes.len() - 5);
619 let mut dec = SeiDecoder::new(&bytes);
620 assert!(dec.next_message().is_err());
621 }
622
623 #[test]
624 fn test_av1_metadata_obu_roundtrip() {
625 let sei_payload = b"av1 sei data".to_vec();
626 let obu = Av1MetadataObu::new(5, sei_payload.clone());
627 let bytes = obu.to_bytes();
628 let parsed = Av1MetadataObu::from_bytes(&bytes).unwrap();
629 assert_eq!(parsed.metadata_type, 5);
630 assert_eq!(parsed.payload, sei_payload);
631 assert!(!parsed.extension_flag);
632 }
633
634 #[test]
635 fn test_av1_metadata_obu_wrong_type() {
636 let bad = &[0x08u8, 0x00, 0x00]; let result = Av1MetadataObu::from_bytes(bad);
639 assert!(result.is_err());
640 }
641
642 #[test]
643 fn test_vp8_metadata_block_roundtrip() {
644 let payload = b"vp8 metadata payload".to_vec();
645 let block = Vp8MetadataBlock::new(payload.clone());
646 let bytes = block.to_bytes();
647 assert_eq!(bytes[0], VP8_USER_DATA_MARKER);
648 let parsed = Vp8MetadataBlock::from_bytes(&bytes).unwrap();
649 assert_eq!(parsed.payload, payload);
650 }
651
652 #[test]
653 fn test_vp8_metadata_block_bad_marker() {
654 let result = Vp8MetadataBlock::from_bytes(&[0x00, 0x00, 0x00, 0x00, 0x00]);
655 assert!(result.is_err());
656 }
657
658 #[test]
659 fn test_sei_payload_type_roundtrip() {
660 for &(byte, expected) in &[
661 (0u8, SeiPayloadType::BufferingPeriod),
662 (1, SeiPayloadType::PictureTiming),
663 (5, SeiPayloadType::UserDataUnregistered),
664 (255, SeiPayloadType::Unknown),
665 ] {
666 assert_eq!(SeiPayloadType::from_byte(byte), expected);
667 }
668 }
669}