1use crate::constants::{ColorPrimaries, MatrixCoefficients, TransferCharacteristics};
2use crate::writer::{Writer, WriterBackend, IO};
3use arrayvec::ArrayVec;
4use std::io::Write;
5use std::num::NonZeroU32;
6use std::{fmt, io};
7
8pub trait MpegBox {
9 fn len(&self) -> usize;
10 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error>;
11}
12
13#[derive(Copy, Clone)]
14pub struct FourCC(pub [u8; 4]);
15
16impl fmt::Debug for FourCC {
17 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
18 match std::str::from_utf8(&self.0) {
19 Ok(s) => s.fmt(f),
20 Err(_) => self.0.fmt(f),
21 }
22 }
23}
24
25#[derive(Debug, Clone)]
26pub struct AvifFile<'data> {
27 pub ftyp: FtypBox,
28 pub meta: MetaBox<'data>,
29 pub mdat: MdatBox,
30}
31
32impl AvifFile<'_> {
33 fn mdat_payload_start_offset(&self) -> u32 {
35 (self.ftyp.len() + self.meta.len()
36 + BASIC_BOX_SIZE) as u32 }
38
39 fn fix_iloc_positions(&mut self) {
42 let start_offset = self.mdat_payload_start_offset();
43 self.meta.iloc.absolute_offset_start = NonZeroU32::new(start_offset);
44 }
45
46 fn write_header(&mut self, out: &mut Vec<u8>) -> io::Result<()> {
47 if self.meta.iprp.ipco.ispe().is_none_or(|b| b.width == 0 || b.height == 0) {
48 return Err(io::Error::new(io::ErrorKind::InvalidInput, "missing width/height"));
49 }
50
51 self.fix_iloc_positions();
52
53 out.try_reserve_exact(self.ftyp.len() + self.meta.len())?;
54 let mut w = Writer::new(out);
55 self.ftyp.write(&mut w).map_err(|_| io::ErrorKind::OutOfMemory)?;
56 self.meta.write(&mut w).map_err(|_| io::ErrorKind::OutOfMemory)?;
57 Ok(())
58 }
59
60 pub fn file_size(&self) -> usize {
61 self.ftyp.len() + self.meta.len() + self.mdat.len(&self.meta.iloc)
62 }
63
64 pub fn write_to_vec(&mut self, out: &mut Vec<u8>) -> io::Result<()> {
65 let expected_file_size = self.file_size();
66 out.try_reserve_exact(expected_file_size)?;
67 let initial = out.len();
68 self.write_header(out)?;
69
70 let _ = self.mdat.write(&mut Writer::new(out), &self.meta.iloc);
71 let written = out.len() - initial;
72 debug_assert_eq!(expected_file_size, written);
73 Ok(())
74 }
75
76 pub fn write<W: Write>(&mut self, mut out: W) -> io::Result<()> {
77 let mut tmp = Vec::new();
78
79 self.write_header(&mut tmp)?;
80 out.write_all(&tmp)?;
81 drop(tmp);
82
83 self.mdat.write(&mut Writer::new(&mut IO(out)), &self.meta.iloc)
84 }
85}
86
87const BASIC_BOX_SIZE: usize = 8;
88const FULL_BOX_SIZE: usize = BASIC_BOX_SIZE + 4;
89
90#[derive(Debug, Clone)]
91pub struct FtypBox {
92 pub major_brand: FourCC,
93 pub minor_version: u32,
94 pub compatible_brands: ArrayVec<FourCC, 2>,
95}
96
97impl MpegBox for FtypBox {
99 #[inline(always)]
100 fn len(&self) -> usize {
101 BASIC_BOX_SIZE
102 + 4 + 4 + 4 * self.compatible_brands.len()
105 }
106
107 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
108 let mut b = w.basic_box(self.len(), *b"ftyp")?;
109 b.push(&self.major_brand.0)?;
110 b.u32(self.minor_version)?;
111 for cb in &self.compatible_brands {
112 b.push(&cb.0)?;
113 }
114 Ok(())
115 }
116}
117
118#[derive(Debug, Clone)]
120pub struct MetaBox<'data> {
121 pub hdlr: HdlrBox,
122 pub iloc: IlocBox<'data>,
123 pub iinf: IinfBox,
124 pub pitm: PitmBox,
125 pub iprp: IprpBox,
126 pub iref: IrefBox,
127}
128
129impl MpegBox for MetaBox<'_> {
130 #[inline]
131 fn len(&self) -> usize {
132 FULL_BOX_SIZE
133 + self.hdlr.len()
134 + self.pitm.len()
135 + self.iloc.len()
136 + self.iinf.len()
137 + self.iprp.len()
138 + if !self.iref.is_empty() { self.iref.len() } else { 0 }
139 }
140
141 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
142 let mut b = w.full_box(self.len(), *b"meta", 0)?;
143 self.hdlr.write(&mut b)?;
144 self.pitm.write(&mut b)?;
145 self.iloc.write(&mut b)?;
146 self.iinf.write(&mut b)?;
147 if !self.iref.is_empty() {
148 self.iref.write(&mut b)?;
149 }
150 self.iprp.write(&mut b)
151 }
152}
153
154#[derive(Debug, Clone)]
156pub struct IinfBox {
157 pub items: ArrayVec<InfeBox, 8>,
158}
159
160impl MpegBox for IinfBox {
161 #[inline]
162 fn len(&self) -> usize {
163 FULL_BOX_SIZE
164 + 2 + self.items.iter().map(|item| item.len()).sum::<usize>()
166 }
167
168 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
169 let mut b = w.full_box(self.len(), *b"iinf", 0)?;
170 b.u16(self.items.len() as _)?;
171 for infe in &self.items {
172 infe.write(&mut b)?;
173 }
174 Ok(())
175 }
176}
177
178#[derive(Debug, Copy, Clone)]
180pub struct InfeBox {
181 pub id: u16,
182 pub typ: FourCC,
183 pub name: &'static str,
184 pub content_type: &'static str,
186}
187
188impl MpegBox for InfeBox {
189 #[inline(always)]
190 fn len(&self) -> usize {
191 FULL_BOX_SIZE
192 + 2 + 2 + 4 + self.name.len() + 1 + if self.content_type.is_empty() { 0 } else { self.content_type.len() + 1 }
197 }
198
199 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
200 let mut b = w.full_box(self.len(), *b"infe", 2)?;
201 b.u16(self.id)?;
202 b.u16(0)?;
203 b.push(&self.typ.0)?;
204 b.push(self.name.as_bytes())?;
205 b.u8(0)?;
206 if !self.content_type.is_empty() {
207 b.push(self.content_type.as_bytes())?;
208 b.u8(0)?;
209 }
210 Ok(())
211 }
212}
213
214#[derive(Debug, Clone)]
215pub struct HdlrBox {
216}
217
218impl MpegBox for HdlrBox {
219 #[inline(always)]
220 fn len(&self) -> usize {
221 FULL_BOX_SIZE + 4 + 4 + 13
222 }
223
224 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
225 let mut b = w.full_box(self.len(), *b"hdlr", 0)?;
228 b.u32(0)?; b.push(b"pict")?; b.u32(0)?; b.u32(0)?; b.u32(0)?; b.u8(0)?; Ok(())
235 }
236}
237
238#[derive(Debug, Clone)]
240pub struct IprpBox {
241 pub ipco: IpcoBox,
242 pub ipma: IpmaBox,
243}
244
245impl MpegBox for IprpBox {
246 #[inline(always)]
247 fn len(&self) -> usize {
248 BASIC_BOX_SIZE
249 + self.ipco.len()
250 + self.ipma.len()
251 }
252
253 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
254 let mut b = w.basic_box(self.len(), *b"iprp")?;
255 self.ipco.write(&mut b)?;
256 self.ipma.write(&mut b)
257 }
258}
259
260#[derive(Debug, Clone)]
261#[non_exhaustive]
262pub enum IpcoProp {
263 Av1C(Av1CBox),
264 Pixi(PixiBox),
265 Ispe(IspeBox),
266 AuxC(AuxCBox),
267 Colr(ColrBox),
268 ColrIcc(ColrIccBox),
269 Clli(ClliBox),
270 Mdcv(MdcvBox),
271 Irot(IrotBox),
272 Imir(ImirBox),
273 Clap(ClapBox),
274 Pasp(PaspBox),
275}
276
277impl IpcoProp {
278 pub fn len(&self) -> usize {
279 match self {
280 Self::Av1C(p) => p.len(),
281 Self::Pixi(p) => p.len(),
282 Self::Ispe(p) => p.len(),
283 Self::AuxC(p) => p.len(),
284 Self::Colr(p) => p.len(),
285 Self::ColrIcc(p) => p.len(),
286 Self::Clli(p) => p.len(),
287 Self::Mdcv(p) => p.len(),
288 Self::Irot(p) => p.len(),
289 Self::Imir(p) => p.len(),
290 Self::Clap(p) => p.len(),
291 Self::Pasp(p) => p.len(),
292 }
293 }
294
295 pub fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
296 match self {
297 Self::Av1C(p) => p.write(w),
298 Self::Pixi(p) => p.write(w),
299 Self::Ispe(p) => p.write(w),
300 Self::AuxC(p) => p.write(w),
301 Self::Colr(p) => p.write(w),
302 Self::ColrIcc(p) => p.write(w),
303 Self::Clli(p) => p.write(w),
304 Self::Mdcv(p) => p.write(w),
305 Self::Irot(p) => p.write(w),
306 Self::Imir(p) => p.write(w),
307 Self::Clap(p) => p.write(w),
308 Self::Pasp(p) => p.write(w),
309 }
310 }
311}
312
313#[derive(Debug, Clone)]
315pub struct IpcoBox {
316 props: ArrayVec<IpcoProp, 16>,
317}
318
319impl IpcoBox {
320 pub fn new() -> Self {
321 Self { props: ArrayVec::new() }
322 }
323
324 #[must_use]
325 pub fn push(&mut self, prop: IpcoProp) -> Option<u8> {
326 self.props.try_push(prop).ok()?;
327 Some(self.props.len() as u8) }
329
330 pub(crate) fn ispe(&self) -> Option<&IspeBox> {
331 self.props.iter().find_map(|b| match b {
332 IpcoProp::Ispe(i) => Some(i),
333 _ => None,
334 })
335 }
336}
337
338impl MpegBox for IpcoBox {
339 #[inline]
340 fn len(&self) -> usize {
341 BASIC_BOX_SIZE
342 + self.props.iter().map(|a| a.len()).sum::<usize>()
343 }
344
345 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
346 let mut b = w.basic_box(self.len(), *b"ipco")?;
347 for p in &self.props {
348 p.write(&mut b)?;
349 }
350 Ok(())
351 }
352}
353
354#[derive(Debug, Copy, Clone)]
355pub struct AuxCBox {
356 pub urn: &'static str,
357}
358
359impl AuxCBox {
360 pub fn len(&self) -> usize {
361 FULL_BOX_SIZE + self.urn.len() + 1
362 }
363
364 pub fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
365 let mut b = w.full_box(self.len(), *b"auxC", 0)?;
366 b.push(self.urn.as_bytes())?;
367 b.u8(0)
368 }
369}
370
371#[derive(Debug, Copy, Clone)]
373pub struct PixiBox {
374 pub depth: u8,
375 pub channels: u8,
376}
377
378impl PixiBox {
379 pub fn len(self) -> usize {
380 FULL_BOX_SIZE
381 + 1 + self.channels as usize
382 }
383
384 pub fn write<B: WriterBackend>(self, w: &mut Writer<B>) -> Result<(), B::Error> {
385 let mut b = w.full_box(self.len(), *b"pixi", 0)?;
386 b.u8(self.channels)?;
387 for _ in 0..self.channels {
388 b.u8(self.depth)?;
389 }
390 Ok(())
391 }
392}
393
394#[derive(Debug, Copy, Clone)]
396pub struct IspeBox {
397 pub width: u32,
398 pub height: u32,
399}
400
401impl MpegBox for IspeBox {
402 #[inline(always)]
403 fn len(&self) -> usize {
404 FULL_BOX_SIZE + 4 + 4
405 }
406
407 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
408 let mut b = w.full_box(self.len(), *b"ispe", 0)?;
409 b.u32(self.width)?;
410 b.u32(self.height)
411 }
412}
413
414#[derive(Debug, Clone)]
416pub struct IpmaEntry {
417 pub item_id: u16,
418 pub prop_ids: ArrayVec<u8, 12>,
419}
420
421#[derive(Debug, Clone)]
422pub struct IpmaBox {
423 pub entries: ArrayVec<IpmaEntry, 4>,
424}
425
426impl MpegBox for IpmaBox {
427 #[inline]
428 fn len(&self) -> usize {
429 FULL_BOX_SIZE + 4 + self.entries.iter().map(|e| 2 + 1 + e.prop_ids.len()).sum::<usize>()
430 }
431
432 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
433 let mut b = w.full_box(self.len(), *b"ipma", 0)?;
434 b.u32(self.entries.len() as _)?; for e in &self.entries {
437 b.u16(e.item_id)?;
438 b.u8(e.prop_ids.len() as u8)?; for &p in &e.prop_ids {
440 b.u8(p)?;
441 }
442 }
443 Ok(())
444 }
445}
446
447#[derive(Debug, Copy, Clone)]
449pub struct IrefEntryBox {
450 pub from_id: u16,
451 pub to_id: u16,
452 pub typ: FourCC,
453}
454
455impl MpegBox for IrefEntryBox {
456 #[inline(always)]
457 fn len(&self) -> usize {
458 BASIC_BOX_SIZE
459 + 2 + 2 + 2 }
463
464 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
465 let mut b = w.basic_box(self.len(), self.typ.0)?;
466 b.u16(self.from_id)?;
467 b.u16(1)?;
468 b.u16(self.to_id)
469 }
470}
471
472#[derive(Debug, Clone)]
478pub struct IrefMultiEntryBox {
479 pub from_id: u16,
480 pub to_ids: ArrayVec<u16, 4>,
481 pub typ: FourCC,
482}
483
484impl MpegBox for IrefMultiEntryBox {
485 #[inline(always)]
486 fn len(&self) -> usize {
487 BASIC_BOX_SIZE
488 + 2 + 2 + 2 * self.to_ids.len() }
492
493 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
494 let mut b = w.basic_box(self.len(), self.typ.0)?;
495 b.u16(self.from_id)?;
496 b.u16(self.to_ids.len() as u16)?;
497 for &to_id in &self.to_ids {
498 b.u16(to_id)?;
499 }
500 Ok(())
501 }
502}
503
504#[derive(Debug, Clone)]
505pub struct IrefBox {
506 pub entries: ArrayVec<IrefEntryBox, 6>,
507 pub multi_entries: ArrayVec<IrefMultiEntryBox, 2>,
508}
509
510impl IrefBox {
511 pub fn is_empty(&self) -> bool {
512 self.entries.is_empty() && self.multi_entries.is_empty()
513 }
514}
515
516impl MpegBox for IrefBox {
517 #[inline(always)]
518 fn len(&self) -> usize {
519 FULL_BOX_SIZE
520 + self.entries.iter().map(|e| e.len()).sum::<usize>()
521 + self.multi_entries.iter().map(|e| e.len()).sum::<usize>()
522 }
523
524 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
525 let mut b = w.full_box(self.len(), *b"iref", 0)?;
526 for entry in &self.entries {
527 entry.write(&mut b)?;
528 }
529 for entry in &self.multi_entries {
530 entry.write(&mut b)?;
531 }
532 Ok(())
533 }
534}
535
536#[derive(Debug, Copy, Clone)]
538#[allow(unused)]
539pub struct AuxlBox {}
540
541impl MpegBox for AuxlBox {
542 #[inline(always)]
543 fn len(&self) -> usize {
544 FULL_BOX_SIZE
545 }
546
547 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
548 w.full_box(self.len(), *b"auxl", 0)?;
549 Ok(())
550 }
551}
552
553#[derive(Debug, Copy, Clone, PartialEq)]
555#[non_exhaustive]
556pub struct ColrBox {
557 pub color_primaries: ColorPrimaries,
558 pub transfer_characteristics: TransferCharacteristics,
559 pub matrix_coefficients: MatrixCoefficients,
560 pub full_range_flag: bool, }
562
563impl Default for ColrBox {
564 fn default() -> Self {
565 Self {
566 color_primaries: ColorPrimaries::Bt709,
567 transfer_characteristics: TransferCharacteristics::Srgb,
568 matrix_coefficients: MatrixCoefficients::Bt601,
569 full_range_flag: true,
570 }
571 }
572}
573
574impl MpegBox for ColrBox {
575 #[inline(always)]
576 fn len(&self) -> usize {
577 BASIC_BOX_SIZE + 4 + 2 + 2 + 2 + 1
578 }
579
580 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
581 let mut b = w.basic_box(self.len(), *b"colr")?;
582 b.u32(u32::from_be_bytes(*b"nclx"))?;
583 b.u16(self.color_primaries as u16)?;
584 b.u16(self.transfer_characteristics as u16)?;
585 b.u16(self.matrix_coefficients as u16)?;
586 b.u8(if self.full_range_flag { 1 << 7 } else { 0 })
587 }
588}
589
590#[derive(Debug, Copy, Clone, PartialEq)]
595#[non_exhaustive]
596pub struct ClliBox {
597 pub max_content_light_level: u16,
599 pub max_pic_average_light_level: u16,
601}
602
603impl ClliBox {
604 pub fn new(max_content_light_level: u16, max_pic_average_light_level: u16) -> Self {
605 Self { max_content_light_level, max_pic_average_light_level }
606 }
607}
608
609impl MpegBox for ClliBox {
610 #[inline(always)]
611 fn len(&self) -> usize {
612 BASIC_BOX_SIZE + 4
613 }
614
615 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
616 let mut b = w.basic_box(self.len(), *b"clli")?;
617 b.u16(self.max_content_light_level)?;
618 b.u16(self.max_pic_average_light_level)
619 }
620}
621
622#[derive(Debug, Copy, Clone, PartialEq)]
627#[non_exhaustive]
628pub struct MdcvBox {
629 pub primaries: [(u16, u16); 3],
633 pub white_point: (u16, u16),
635 pub max_luminance: u32,
638 pub min_luminance: u32,
641}
642
643impl MdcvBox {
644 pub fn new(primaries: [(u16, u16); 3], white_point: (u16, u16), max_luminance: u32, min_luminance: u32) -> Self {
645 Self { primaries, white_point, max_luminance, min_luminance }
646 }
647}
648
649impl MpegBox for MdcvBox {
650 #[inline(always)]
651 fn len(&self) -> usize {
652 BASIC_BOX_SIZE + 24
653 }
654
655 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
656 let mut b = w.basic_box(self.len(), *b"mdcv")?;
657 for &(x, y) in &self.primaries {
658 b.u16(x)?;
659 b.u16(y)?;
660 }
661 b.u16(self.white_point.0)?;
662 b.u16(self.white_point.1)?;
663 b.u32(self.max_luminance)?;
664 b.u32(self.min_luminance)
665 }
666}
667
668#[derive(Debug, Copy, Clone)]
669#[non_exhaustive]
670pub struct Av1CBox {
671 pub seq_profile: u8,
672 pub seq_level_idx_0: u8,
673 pub seq_tier_0: bool,
674 pub high_bitdepth: bool,
675 pub twelve_bit: bool,
676 pub monochrome: bool,
677 pub chroma_subsampling_x: bool,
678 pub chroma_subsampling_y: bool,
679 pub chroma_sample_position: u8,
680}
681
682impl Default for Av1CBox {
683 fn default() -> Self {
685 Self {
686 seq_profile: 0,
687 seq_level_idx_0: 4,
688 seq_tier_0: false,
689 high_bitdepth: false,
690 twelve_bit: false,
691 monochrome: false,
692 chroma_subsampling_x: true,
693 chroma_subsampling_y: true,
694 chroma_sample_position: 0,
695 }
696 }
697}
698
699impl MpegBox for Av1CBox {
700 #[inline(always)]
701 fn len(&self) -> usize {
702 BASIC_BOX_SIZE + 4
703 }
704
705 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
706 let mut b = w.basic_box(self.len(), *b"av1C")?;
707 let flags1 =
708 u8::from(self.seq_tier_0) << 7 |
709 u8::from(self.high_bitdepth) << 6 |
710 u8::from(self.twelve_bit) << 5 |
711 u8::from(self.monochrome) << 4 |
712 u8::from(self.chroma_subsampling_x) << 3 |
713 u8::from(self.chroma_subsampling_y) << 2 |
714 self.chroma_sample_position;
715
716 b.push(&[
717 0x81, (self.seq_profile << 5) | self.seq_level_idx_0, flags1,
720 0,
721 ])
722 }
723}
724
725#[derive(Debug, Copy, Clone)]
726pub struct PitmBox(pub u16);
727
728impl MpegBox for PitmBox {
729 #[inline(always)]
730 fn len(&self) -> usize {
731 FULL_BOX_SIZE + 2
732 }
733
734 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
735 let mut b = w.full_box(self.len(), *b"pitm", 0)?;
736 b.u16(self.0)
737 }
738}
739
740#[derive(Debug, Clone)]
741pub struct IlocBox<'data> {
742 pub absolute_offset_start: Option<NonZeroU32>,
744 pub items: ArrayVec<IlocItem<'data>, 8>,
745}
746
747#[derive(Debug, Clone)]
748pub struct IlocItem<'data> {
749 pub id: u16,
750 pub extents: [IlocExtent<'data>; 1],
751}
752
753#[derive(Debug, Copy, Clone)]
754pub struct IlocExtent<'data> {
755 pub data: &'data [u8],
757}
758
759impl MpegBox for IlocBox<'_> {
760 #[inline(always)]
761 #[allow(unused_parens, clippy::identity_op)]
762 fn len(&self) -> usize {
763 FULL_BOX_SIZE
764 + 1 + 1 + 2 + self.items.iter().map(|i| ( 2 + 2 + 0 + 2 + i.extents.len() * ( 4 + 4 )
776 )).sum::<usize>()
777 }
778
779 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
780 let mut b = w.full_box(self.len(), *b"iloc", 0)?;
781 b.push(&[4 << 4 | 4, 0])?; b.u16(self.items.len() as _)?; let mut next_start = if let Some(ok) = self.absolute_offset_start { ok.get() } else {
785 debug_assert!(false);
786 !0
787 };
788 for item in &self.items {
789 b.u16(item.id)?;
790 b.u16(0)?;
791 b.u16(item.extents.len() as _)?; for ex in &item.extents {
793 let len = ex.data.len() as u32;
794 b.u32(next_start)?;
795 next_start += len;
796 b.u32(len)?;
797 }
798 }
799 Ok(())
800 }
801}
802
803#[derive(Debug, Copy, Clone, PartialEq)]
808#[non_exhaustive]
809pub struct IrotBox {
810 pub angle: u8,
812}
813
814impl IrotBox {
815 pub fn new(angle: u8) -> Self { Self { angle } }
816}
817
818impl MpegBox for IrotBox {
819 #[inline(always)]
820 fn len(&self) -> usize {
821 BASIC_BOX_SIZE + 1
822 }
823
824 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
825 let mut b = w.basic_box(self.len(), *b"irot")?;
826 b.u8(self.angle & 0x03)
827 }
828}
829
830#[derive(Debug, Copy, Clone, PartialEq)]
836#[non_exhaustive]
837pub struct ImirBox {
838 pub axis: u8,
840}
841
842impl ImirBox {
843 pub fn new(axis: u8) -> Self { Self { axis } }
844}
845
846impl MpegBox for ImirBox {
847 #[inline(always)]
848 fn len(&self) -> usize {
849 BASIC_BOX_SIZE + 1
850 }
851
852 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
853 let mut b = w.basic_box(self.len(), *b"imir")?;
854 b.u8(self.axis & 0x01)
855 }
856}
857
858#[derive(Debug, Copy, Clone, PartialEq)]
864#[non_exhaustive]
865pub struct ClapBox {
866 pub width_n: u32,
867 pub width_d: u32,
868 pub height_n: u32,
869 pub height_d: u32,
870 pub horiz_off_n: i32,
871 pub horiz_off_d: u32,
872 pub vert_off_n: i32,
873 pub vert_off_d: u32,
874}
875
876impl ClapBox {
877 #[allow(clippy::too_many_arguments)]
879 pub fn new(width_n: u32, width_d: u32, height_n: u32, height_d: u32,
880 horiz_off_n: i32, horiz_off_d: u32, vert_off_n: i32, vert_off_d: u32) -> Self {
881 Self { width_n, width_d, height_n, height_d, horiz_off_n, horiz_off_d, vert_off_n, vert_off_d }
882 }
883}
884
885impl MpegBox for ClapBox {
886 #[inline(always)]
887 fn len(&self) -> usize {
888 BASIC_BOX_SIZE + 32
889 }
890
891 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
892 let mut b = w.basic_box(self.len(), *b"clap")?;
893 b.u32(self.width_n)?;
894 b.u32(self.width_d)?;
895 b.u32(self.height_n)?;
896 b.u32(self.height_d)?;
897 b.push(&self.horiz_off_n.to_be_bytes())?;
898 b.u32(self.horiz_off_d)?;
899 b.push(&self.vert_off_n.to_be_bytes())?;
900 b.u32(self.vert_off_d)
901 }
902}
903
904#[derive(Debug, Copy, Clone, PartialEq)]
908#[non_exhaustive]
909pub struct PaspBox {
910 pub h_spacing: u32,
911 pub v_spacing: u32,
912}
913
914impl PaspBox {
915 pub fn new(h_spacing: u32, v_spacing: u32) -> Self { Self { h_spacing, v_spacing } }
916}
917
918impl MpegBox for PaspBox {
919 #[inline(always)]
920 fn len(&self) -> usize {
921 BASIC_BOX_SIZE + 8
922 }
923
924 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
925 let mut b = w.basic_box(self.len(), *b"pasp")?;
926 b.u32(self.h_spacing)?;
927 b.u32(self.v_spacing)
928 }
929}
930
931#[derive(Debug, Clone)]
933#[non_exhaustive]
934pub struct ColrIccBox {
935 pub icc_data: Vec<u8>,
936}
937
938impl ColrIccBox {
939 pub fn new(icc_data: Vec<u8>) -> Self { Self { icc_data } }
940}
941
942impl MpegBox for ColrIccBox {
943 #[inline(always)]
944 fn len(&self) -> usize {
945 BASIC_BOX_SIZE + 4 + self.icc_data.len()
946 }
947
948 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
949 let mut b = w.basic_box(self.len(), *b"colr")?;
950 b.u32(u32::from_be_bytes(*b"prof"))?;
952 b.push(&self.icc_data)
953 }
954}
955
956#[derive(Debug, Clone)]
957pub struct MdatBox;
958
959impl MdatBox {
960 #[inline(always)]
961 fn len(&self, chunks: &IlocBox) -> usize {
962 BASIC_BOX_SIZE + chunks.items.iter().flat_map(|c| &c.extents).map(|d| d.data.len()).sum::<usize>()
963 }
964
965 fn write<B: WriterBackend>(&self, w: &mut Writer<B>, chunks: &IlocBox) -> Result<(), B::Error> {
966 let mut b = w.basic_box(self.len(chunks), *b"mdat")?;
967 for ch in chunks.items.iter().flat_map(|c| &c.extents) {
968 b.push(ch.data)?;
969 }
970 Ok(())
971 }
972}