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().map_or(true, |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, 3>,
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}
185
186impl MpegBox for InfeBox {
187 #[inline(always)]
188 fn len(&self) -> usize {
189 FULL_BOX_SIZE
190 + 2 + 2 + 4 + self.name.len() + 1 }
195
196 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
197 let mut b = w.full_box(self.len(), *b"infe", 2)?;
198 b.u16(self.id)?;
199 b.u16(0)?;
200 b.push(&self.typ.0)?;
201 b.push(self.name.as_bytes())?;
202 b.u8(0)
203 }
204}
205
206#[derive(Debug, Clone)]
207pub struct HdlrBox {
208}
209
210impl MpegBox for HdlrBox {
211 #[inline(always)]
212 fn len(&self) -> usize {
213 FULL_BOX_SIZE + 4 + 4 + 13
214 }
215
216 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
217 let mut b = w.full_box(self.len(), *b"hdlr", 0)?;
220 b.u32(0)?; b.push(b"pict")?; b.u32(0)?; b.u32(0)?; b.u32(0)?; b.u8(0)?; Ok(())
227 }
228}
229
230#[derive(Debug, Clone)]
232pub struct IprpBox {
233 pub ipco: IpcoBox,
234 pub ipma: IpmaBox,
235}
236
237impl MpegBox for IprpBox {
238 #[inline(always)]
239 fn len(&self) -> usize {
240 BASIC_BOX_SIZE
241 + self.ipco.len()
242 + self.ipma.len()
243 }
244
245 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
246 let mut b = w.basic_box(self.len(), *b"iprp")?;
247 self.ipco.write(&mut b)?;
248 self.ipma.write(&mut b)
249 }
250}
251
252#[derive(Debug, Clone)]
253#[non_exhaustive]
254pub enum IpcoProp {
255 Av1C(Av1CBox),
256 Pixi(PixiBox),
257 Ispe(IspeBox),
258 AuxC(AuxCBox),
259 Colr(ColrBox),
260}
261
262impl IpcoProp {
263 pub fn len(&self) -> usize {
264 match self {
265 Self::Av1C(p) => p.len(),
266 Self::Pixi(p) => p.len(),
267 Self::Ispe(p) => p.len(),
268 Self::AuxC(p) => p.len(),
269 Self::Colr(p) => p.len(),
270 }
271 }
272
273 pub fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
274 match self {
275 Self::Av1C(p) => p.write(w),
276 Self::Pixi(p) => p.write(w),
277 Self::Ispe(p) => p.write(w),
278 Self::AuxC(p) => p.write(w),
279 Self::Colr(p) => p.write(w),
280 }
281 }
282}
283
284#[derive(Debug, Clone)]
286pub struct IpcoBox {
287 props: ArrayVec<IpcoProp, 7>,
288}
289
290impl IpcoBox {
291 pub fn new() -> Self {
292 Self { props: ArrayVec::new() }
293 }
294
295 #[must_use]
296 pub fn push(&mut self, prop: IpcoProp) -> Option<u8> {
297 self.props.try_push(prop).ok()?;
298 Some(self.props.len() as u8) }
300
301 pub(crate) fn ispe(&self) -> Option<&IspeBox> {
302 self.props.iter().find_map(|b| match b {
303 IpcoProp::Ispe(i) => Some(i),
304 _ => None,
305 })
306 }
307}
308
309impl MpegBox for IpcoBox {
310 #[inline]
311 fn len(&self) -> usize {
312 BASIC_BOX_SIZE
313 + self.props.iter().map(|a| a.len()).sum::<usize>()
314 }
315
316 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
317 let mut b = w.basic_box(self.len(), *b"ipco")?;
318 for p in &self.props {
319 p.write(&mut b)?;
320 }
321 Ok(())
322 }
323}
324
325#[derive(Debug, Copy, Clone)]
326pub struct AuxCBox {
327 pub urn: &'static str,
328}
329
330impl AuxCBox {
331 pub fn len(&self) -> usize {
332 FULL_BOX_SIZE + self.urn.len() + 1
333 }
334
335 pub fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
336 let mut b = w.full_box(self.len(), *b"auxC", 0)?;
337 b.push(self.urn.as_bytes())?;
338 b.u8(0)
339 }
340}
341
342#[derive(Debug, Copy, Clone)]
344pub struct PixiBox {
345 pub depth: u8,
346 pub channels: u8,
347}
348
349impl PixiBox {
350 pub fn len(self) -> usize {
351 FULL_BOX_SIZE
352 + 1 + self.channels as usize
353 }
354
355 pub fn write<B: WriterBackend>(self, w: &mut Writer<B>) -> Result<(), B::Error> {
356 let mut b = w.full_box(self.len(), *b"pixi", 0)?;
357 b.u8(self.channels)?;
358 for _ in 0..self.channels {
359 b.u8(self.depth)?;
360 }
361 Ok(())
362 }
363}
364
365#[derive(Debug, Copy, Clone)]
367pub struct IspeBox {
368 pub width: u32,
369 pub height: u32,
370}
371
372impl MpegBox for IspeBox {
373 #[inline(always)]
374 fn len(&self) -> usize {
375 FULL_BOX_SIZE + 4 + 4
376 }
377
378 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
379 let mut b = w.full_box(self.len(), *b"ispe", 0)?;
380 b.u32(self.width)?;
381 b.u32(self.height)
382 }
383}
384
385#[derive(Debug, Clone)]
387pub struct IpmaEntry {
388 pub item_id: u16,
389 pub prop_ids: ArrayVec<u8, 5>,
390}
391
392#[derive(Debug, Clone)]
393pub struct IpmaBox {
394 pub entries: ArrayVec<IpmaEntry, 2>,
395}
396
397impl MpegBox for IpmaBox {
398 #[inline]
399 fn len(&self) -> usize {
400 FULL_BOX_SIZE + 4 + self.entries.iter().map(|e| 2 + 1 + e.prop_ids.len()).sum::<usize>()
401 }
402
403 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
404 let mut b = w.full_box(self.len(), *b"ipma", 0)?;
405 b.u32(self.entries.len() as _)?; for e in &self.entries {
408 b.u16(e.item_id)?;
409 b.u8(e.prop_ids.len() as u8)?; for &p in &e.prop_ids {
411 b.u8(p)?;
412 }
413 }
414 Ok(())
415 }
416}
417
418#[derive(Debug, Copy, Clone)]
420pub struct IrefEntryBox {
421 pub from_id: u16,
422 pub to_id: u16,
423 pub typ: FourCC,
424}
425
426impl MpegBox for IrefEntryBox {
427 #[inline(always)]
428 fn len(&self) -> usize {
429 BASIC_BOX_SIZE
430 + 2 + 2 + 2 }
434
435 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
436 let mut b = w.basic_box(self.len(), self.typ.0)?;
437 b.u16(self.from_id)?;
438 b.u16(1)?;
439 b.u16(self.to_id)
440 }
441}
442
443#[derive(Debug, Clone)]
444pub struct IrefBox {
445 pub entries: ArrayVec<IrefEntryBox, 3>,
446}
447
448impl IrefBox {
449 pub fn is_empty(&self) -> bool {
450 self.entries.is_empty()
451 }
452}
453
454impl MpegBox for IrefBox {
455 #[inline(always)]
456 fn len(&self) -> usize {
457 FULL_BOX_SIZE + self.entries.iter().map(|e| e.len()).sum::<usize>()
458 }
459
460 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
461 let mut b = w.full_box(self.len(), *b"iref", 0)?;
462 for entry in &self.entries {
463 entry.write(&mut b)?;
464 }
465 Ok(())
466 }
467}
468
469#[derive(Debug, Copy, Clone)]
471#[allow(unused)]
472pub struct AuxlBox {}
473
474impl MpegBox for AuxlBox {
475 #[inline(always)]
476 fn len(&self) -> usize {
477 FULL_BOX_SIZE
478 }
479
480 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
481 w.full_box(self.len(), *b"auxl", 0)?;
482 Ok(())
483 }
484}
485
486#[derive(Debug, Copy, Clone, PartialEq)]
488pub struct ColrBox {
489 pub color_primaries: ColorPrimaries,
490 pub transfer_characteristics: TransferCharacteristics,
491 pub matrix_coefficients: MatrixCoefficients,
492 pub full_range_flag: bool, }
494
495impl Default for ColrBox {
496 fn default() -> Self {
497 Self {
498 color_primaries: ColorPrimaries::Bt709,
499 transfer_characteristics: TransferCharacteristics::Srgb,
500 matrix_coefficients: MatrixCoefficients::Bt601,
501 full_range_flag: true,
502 }
503 }
504}
505
506impl MpegBox for ColrBox {
507 #[inline(always)]
508 fn len(&self) -> usize {
509 BASIC_BOX_SIZE + 4 + 2 + 2 + 2 + 1
510 }
511
512 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
513 let mut b = w.basic_box(self.len(), *b"colr")?;
514 b.u32(u32::from_be_bytes(*b"nclx"))?;
515 b.u16(self.color_primaries as u16)?;
516 b.u16(self.transfer_characteristics as u16)?;
517 b.u16(self.matrix_coefficients as u16)?;
518 b.u8(if self.full_range_flag { 1 << 7 } else { 0 })
519 }
520}
521#[derive(Debug, Copy, Clone)]
522pub struct Av1CBox {
523 pub seq_profile: u8,
524 pub seq_level_idx_0: u8,
525 pub seq_tier_0: bool,
526 pub high_bitdepth: bool,
527 pub twelve_bit: bool,
528 pub monochrome: bool,
529 pub chroma_subsampling_x: bool,
530 pub chroma_subsampling_y: bool,
531 pub chroma_sample_position: u8,
532}
533
534impl MpegBox for Av1CBox {
535 #[inline(always)]
536 fn len(&self) -> usize {
537 BASIC_BOX_SIZE + 4
538 }
539
540 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
541 let mut b = w.basic_box(self.len(), *b"av1C")?;
542 let flags1 =
543 u8::from(self.seq_tier_0) << 7 |
544 u8::from(self.high_bitdepth) << 6 |
545 u8::from(self.twelve_bit) << 5 |
546 u8::from(self.monochrome) << 4 |
547 u8::from(self.chroma_subsampling_x) << 3 |
548 u8::from(self.chroma_subsampling_y) << 2 |
549 self.chroma_sample_position;
550
551 b.push(&[
552 0x81, (self.seq_profile << 5) | self.seq_level_idx_0, flags1,
555 0,
556 ])
557 }
558}
559
560#[derive(Debug, Copy, Clone)]
561pub struct PitmBox(pub u16);
562
563impl MpegBox for PitmBox {
564 #[inline(always)]
565 fn len(&self) -> usize {
566 FULL_BOX_SIZE + 2
567 }
568
569 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
570 let mut b = w.full_box(self.len(), *b"pitm", 0)?;
571 b.u16(self.0)
572 }
573}
574
575#[derive(Debug, Clone)]
576pub struct IlocBox<'data> {
577 pub absolute_offset_start: Option<NonZeroU32>,
579 pub items: ArrayVec<IlocItem<'data>, 3>,
580}
581
582#[derive(Debug, Clone)]
583pub struct IlocItem<'data> {
584 pub id: u16,
585 pub extents: [IlocExtent<'data>; 1],
586}
587
588#[derive(Debug, Copy, Clone)]
589pub struct IlocExtent<'data> {
590 pub data: &'data [u8],
592}
593
594impl MpegBox for IlocBox<'_> {
595 #[inline(always)]
596 #[allow(unused_parens)]
597 fn len(&self) -> usize {
598 FULL_BOX_SIZE
599 + 1 + 1 + 2 + self.items.iter().map(|i| ( 2 + 2 + 0 + 2 + i.extents.len() * ( 4 + 4 )
611 )).sum::<usize>()
612 }
613
614 fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> {
615 let mut b = w.full_box(self.len(), *b"iloc", 0)?;
616 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 {
620 debug_assert!(false);
621 !0
622 };
623 for item in &self.items {
624 b.u16(item.id)?;
625 b.u16(0)?;
626 b.u16(item.extents.len() as _)?; for ex in &item.extents {
628 let len = ex.data.len() as u32;
629 b.u32(next_start)?;
630 next_start += len;
631 b.u32(len)?;
632 }
633 }
634 Ok(())
635 }
636}
637
638#[derive(Debug, Clone)]
639pub struct MdatBox;
640
641impl MdatBox {
642 #[inline(always)]
643 fn len(&self, chunks: &IlocBox) -> usize {
644 BASIC_BOX_SIZE + chunks.items.iter().flat_map(|c| &c.extents).map(|d| d.data.len()).sum::<usize>()
645 }
646
647 fn write<B: WriterBackend>(&self, w: &mut Writer<B>, chunks: &IlocBox) -> Result<(), B::Error> {
648 let mut b = w.basic_box(self.len(chunks), *b"mdat")?;
649 for ch in chunks.items.iter().flat_map(|c| &c.extents) {
650 b.push(ch.data)?;
651 }
652 Ok(())
653 }
654}