/rust/registry/src/index.crates.io-1949cf8c6b5b557f/avif-serialize-0.8.8/src/boxes.rs
Line | Count | Source |
1 | | use crate::constants::{ColorPrimaries, MatrixCoefficients, TransferCharacteristics}; |
2 | | use crate::writer::{Writer, WriterBackend, IO}; |
3 | | use arrayvec::ArrayVec; |
4 | | use std::io::Write; |
5 | | use std::num::NonZeroU32; |
6 | | use std::{fmt, io}; |
7 | | |
8 | | pub 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)] |
14 | | pub struct FourCC(pub [u8; 4]); |
15 | | |
16 | | impl fmt::Debug for FourCC { |
17 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
18 | 0 | match std::str::from_utf8(&self.0) { |
19 | 0 | Ok(s) => s.fmt(f), |
20 | 0 | Err(_) => self.0.fmt(f), |
21 | | } |
22 | 0 | } |
23 | | } |
24 | | |
25 | | #[derive(Debug, Clone)] |
26 | | pub struct AvifFile<'data> { |
27 | | pub ftyp: FtypBox, |
28 | | pub meta: MetaBox<'data>, |
29 | | pub mdat: MdatBox, |
30 | | } |
31 | | |
32 | | impl AvifFile<'_> { |
33 | | /// Where the primary data starts inside the `mdat` box, for `iloc`'s offset |
34 | 0 | fn mdat_payload_start_offset(&self) -> u32 { |
35 | 0 | (self.ftyp.len() + self.meta.len() |
36 | 0 | + BASIC_BOX_SIZE) as u32 // mdat head |
37 | 0 | } |
38 | | |
39 | | /// `iloc` is mostly unnecssary, high risk of out-of-buffer accesses in parsers that don't pay attention, |
40 | | /// and also awkward to serialize, because its content depends on its own serialized byte size. |
41 | 0 | fn fix_iloc_positions(&mut self) { |
42 | 0 | let start_offset = self.mdat_payload_start_offset(); |
43 | 0 | self.meta.iloc.absolute_offset_start = NonZeroU32::new(start_offset); |
44 | 0 | } |
45 | | |
46 | 0 | fn write_header(&mut self, out: &mut Vec<u8>) -> io::Result<()> { |
47 | 0 | if self.meta.iprp.ipco.ispe().is_none_or(|b| b.width == 0 || b.height == 0) { |
48 | 0 | return Err(io::Error::new(io::ErrorKind::InvalidInput, "missing width/height")); |
49 | 0 | } |
50 | | |
51 | 0 | self.fix_iloc_positions(); |
52 | | |
53 | 0 | out.try_reserve_exact(self.ftyp.len() + self.meta.len())?; |
54 | 0 | let mut w = Writer::new(out); |
55 | 0 | self.ftyp.write(&mut w).map_err(|_| io::ErrorKind::OutOfMemory)?; |
56 | 0 | self.meta.write(&mut w).map_err(|_| io::ErrorKind::OutOfMemory)?; |
57 | 0 | Ok(()) |
58 | 0 | } |
59 | | |
60 | 0 | pub fn file_size(&self) -> usize { |
61 | 0 | self.ftyp.len() + self.meta.len() + self.mdat.len(&self.meta.iloc) |
62 | 0 | } |
63 | | |
64 | 0 | pub fn write_to_vec(&mut self, out: &mut Vec<u8>) -> io::Result<()> { |
65 | 0 | let expected_file_size = self.file_size(); |
66 | 0 | out.try_reserve_exact(expected_file_size)?; |
67 | 0 | let initial = out.len(); |
68 | 0 | self.write_header(out)?; |
69 | | |
70 | 0 | let _ = self.mdat.write(&mut Writer::new(out), &self.meta.iloc); |
71 | 0 | let written = out.len() - initial; |
72 | 0 | debug_assert_eq!(expected_file_size, written); |
73 | 0 | Ok(()) |
74 | 0 | } |
75 | | |
76 | 0 | pub fn write<W: Write>(&mut self, mut out: W) -> io::Result<()> { |
77 | 0 | let mut tmp = Vec::new(); |
78 | | |
79 | 0 | self.write_header(&mut tmp)?; |
80 | 0 | out.write_all(&tmp)?; |
81 | 0 | drop(tmp); |
82 | | |
83 | 0 | self.mdat.write(&mut Writer::new(&mut IO(out)), &self.meta.iloc) |
84 | 0 | } |
85 | | } |
86 | | |
87 | | const BASIC_BOX_SIZE: usize = 8; |
88 | | const FULL_BOX_SIZE: usize = BASIC_BOX_SIZE + 4; |
89 | | |
90 | | #[derive(Debug, Clone)] |
91 | | pub struct FtypBox { |
92 | | pub major_brand: FourCC, |
93 | | pub minor_version: u32, |
94 | | pub compatible_brands: ArrayVec<FourCC, 2>, |
95 | | } |
96 | | |
97 | | /// File Type box (chunk) |
98 | | impl MpegBox for FtypBox { |
99 | | #[inline(always)] |
100 | 0 | fn len(&self) -> usize { |
101 | 0 | BASIC_BOX_SIZE |
102 | 0 | + 4 // brand |
103 | 0 | + 4 // ver |
104 | 0 | + 4 * self.compatible_brands.len() |
105 | 0 | } |
106 | | |
107 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
108 | 0 | let mut b = w.basic_box(self.len(), *b"ftyp")?; |
109 | 0 | b.push(&self.major_brand.0)?; |
110 | 0 | b.u32(self.minor_version)?; |
111 | 0 | for cb in &self.compatible_brands { |
112 | 0 | b.push(&cb.0)?; |
113 | | } |
114 | 0 | Ok(()) |
115 | 0 | } |
116 | | } |
117 | | |
118 | | /// Metadata box |
119 | | #[derive(Debug, Clone)] |
120 | | pub 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 | | |
129 | | impl MpegBox for MetaBox<'_> { |
130 | | #[inline] |
131 | 0 | fn len(&self) -> usize { |
132 | 0 | FULL_BOX_SIZE |
133 | 0 | + self.hdlr.len() |
134 | 0 | + self.pitm.len() |
135 | 0 | + self.iloc.len() |
136 | 0 | + self.iinf.len() |
137 | 0 | + self.iprp.len() |
138 | 0 | + if !self.iref.is_empty() { self.iref.len() } else { 0 } |
139 | 0 | } |
140 | | |
141 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
142 | 0 | let mut b = w.full_box(self.len(), *b"meta", 0)?; |
143 | 0 | self.hdlr.write(&mut b)?; |
144 | 0 | self.pitm.write(&mut b)?; |
145 | 0 | self.iloc.write(&mut b)?; |
146 | 0 | self.iinf.write(&mut b)?; |
147 | 0 | if !self.iref.is_empty() { |
148 | 0 | self.iref.write(&mut b)?; |
149 | 0 | } |
150 | 0 | self.iprp.write(&mut b) |
151 | 0 | } |
152 | | } |
153 | | |
154 | | /// Item Info box |
155 | | #[derive(Debug, Clone)] |
156 | | pub struct IinfBox { |
157 | | pub items: ArrayVec<InfeBox, 3>, |
158 | | } |
159 | | |
160 | | impl MpegBox for IinfBox { |
161 | | #[inline] |
162 | 0 | fn len(&self) -> usize { |
163 | 0 | FULL_BOX_SIZE |
164 | 0 | + 2 // num items u16 |
165 | 0 | + self.items.iter().map(|item| item.len()).sum::<usize>() |
166 | 0 | } |
167 | | |
168 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
169 | 0 | let mut b = w.full_box(self.len(), *b"iinf", 0)?; |
170 | 0 | b.u16(self.items.len() as _)?; |
171 | 0 | for infe in &self.items { |
172 | 0 | infe.write(&mut b)?; |
173 | | } |
174 | 0 | Ok(()) |
175 | 0 | } |
176 | | } |
177 | | |
178 | | /// Item Info Entry box |
179 | | #[derive(Debug, Copy, Clone)] |
180 | | pub struct InfeBox { |
181 | | pub id: u16, |
182 | | pub typ: FourCC, |
183 | | pub name: &'static str, |
184 | | } |
185 | | |
186 | | impl MpegBox for InfeBox { |
187 | | #[inline(always)] |
188 | 0 | fn len(&self) -> usize { |
189 | 0 | FULL_BOX_SIZE |
190 | 0 | + 2 // id |
191 | 0 | + 2 // item_protection_index |
192 | 0 | + 4 // type |
193 | 0 | + self.name.len() + 1 // nul-terminated |
194 | 0 | } |
195 | | |
196 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
197 | 0 | let mut b = w.full_box(self.len(), *b"infe", 2)?; |
198 | 0 | b.u16(self.id)?; |
199 | 0 | b.u16(0)?; |
200 | 0 | b.push(&self.typ.0)?; |
201 | 0 | b.push(self.name.as_bytes())?; |
202 | 0 | b.u8(0) |
203 | 0 | } |
204 | | } |
205 | | |
206 | | #[derive(Debug, Clone)] |
207 | | pub struct HdlrBox { |
208 | | } |
209 | | |
210 | | impl MpegBox for HdlrBox { |
211 | | #[inline(always)] |
212 | 0 | fn len(&self) -> usize { |
213 | 0 | FULL_BOX_SIZE + 4 + 4 + 13 |
214 | 0 | } |
215 | | |
216 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
217 | | // because an image format needs to be told it's an image format, |
218 | | // and it does it the way classic MacOS used to, because Quicktime. |
219 | 0 | let mut b = w.full_box(self.len(), *b"hdlr", 0)?; |
220 | 0 | b.u32(0)?; // old MacOS file type handler |
221 | 0 | b.push(b"pict")?; // MacOS Quicktime subtype |
222 | 0 | b.u32(0)?; // Firefox 92 wants all 0 here |
223 | 0 | b.u32(0)?; // Reserved |
224 | 0 | b.u32(0)?; // Reserved |
225 | 0 | b.u8(0)?; // Pascal string for component name |
226 | 0 | Ok(()) |
227 | 0 | } |
228 | | } |
229 | | |
230 | | /// Item properties + associations |
231 | | #[derive(Debug, Clone)] |
232 | | pub struct IprpBox { |
233 | | pub ipco: IpcoBox, |
234 | | pub ipma: IpmaBox, |
235 | | } |
236 | | |
237 | | impl MpegBox for IprpBox { |
238 | | #[inline(always)] |
239 | 0 | fn len(&self) -> usize { |
240 | 0 | BASIC_BOX_SIZE |
241 | 0 | + self.ipco.len() |
242 | 0 | + self.ipma.len() |
243 | 0 | } |
244 | | |
245 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
246 | 0 | let mut b = w.basic_box(self.len(), *b"iprp")?; |
247 | 0 | self.ipco.write(&mut b)?; |
248 | 0 | self.ipma.write(&mut b) |
249 | 0 | } |
250 | | } |
251 | | |
252 | | #[derive(Debug, Clone)] |
253 | | #[non_exhaustive] |
254 | | pub enum IpcoProp { |
255 | | Av1C(Av1CBox), |
256 | | Pixi(PixiBox), |
257 | | Ispe(IspeBox), |
258 | | AuxC(AuxCBox), |
259 | | Colr(ColrBox), |
260 | | Clli(ClliBox), |
261 | | Mdcv(MdcvBox), |
262 | | } |
263 | | |
264 | | impl IpcoProp { |
265 | 0 | pub fn len(&self) -> usize { |
266 | 0 | match self { |
267 | 0 | Self::Av1C(p) => p.len(), |
268 | 0 | Self::Pixi(p) => p.len(), |
269 | 0 | Self::Ispe(p) => p.len(), |
270 | 0 | Self::AuxC(p) => p.len(), |
271 | 0 | Self::Colr(p) => p.len(), |
272 | 0 | Self::Clli(p) => p.len(), |
273 | 0 | Self::Mdcv(p) => p.len(), |
274 | | } |
275 | 0 | } |
276 | | |
277 | 0 | pub fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
278 | 0 | match self { |
279 | 0 | Self::Av1C(p) => p.write(w), |
280 | 0 | Self::Pixi(p) => p.write(w), |
281 | 0 | Self::Ispe(p) => p.write(w), |
282 | 0 | Self::AuxC(p) => p.write(w), |
283 | 0 | Self::Colr(p) => p.write(w), |
284 | 0 | Self::Clli(p) => p.write(w), |
285 | 0 | Self::Mdcv(p) => p.write(w), |
286 | | } |
287 | 0 | } |
288 | | } |
289 | | |
290 | | /// Item Property Container box |
291 | | #[derive(Debug, Clone)] |
292 | | pub struct IpcoBox { |
293 | | props: ArrayVec<IpcoProp, 9>, |
294 | | } |
295 | | |
296 | | impl IpcoBox { |
297 | 0 | pub fn new() -> Self { |
298 | 0 | Self { props: ArrayVec::new() } |
299 | 0 | } |
300 | | |
301 | | #[must_use] |
302 | 0 | pub fn push(&mut self, prop: IpcoProp) -> Option<u8> { |
303 | 0 | self.props.try_push(prop).ok()?; |
304 | 0 | Some(self.props.len() as u8) // the spec wants them off by one |
305 | 0 | } |
306 | | |
307 | 0 | pub(crate) fn ispe(&self) -> Option<&IspeBox> { |
308 | 0 | self.props.iter().find_map(|b| match b { |
309 | 0 | IpcoProp::Ispe(i) => Some(i), |
310 | 0 | _ => None, |
311 | 0 | }) |
312 | 0 | } |
313 | | } |
314 | | |
315 | | impl MpegBox for IpcoBox { |
316 | | #[inline] |
317 | 0 | fn len(&self) -> usize { |
318 | | BASIC_BOX_SIZE |
319 | 0 | + self.props.iter().map(|a| a.len()).sum::<usize>() |
320 | 0 | } |
321 | | |
322 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
323 | 0 | let mut b = w.basic_box(self.len(), *b"ipco")?; |
324 | 0 | for p in &self.props { |
325 | 0 | p.write(&mut b)?; |
326 | | } |
327 | 0 | Ok(()) |
328 | 0 | } |
329 | | } |
330 | | |
331 | | #[derive(Debug, Copy, Clone)] |
332 | | pub struct AuxCBox { |
333 | | pub urn: &'static str, |
334 | | } |
335 | | |
336 | | impl AuxCBox { |
337 | 0 | pub fn len(&self) -> usize { |
338 | 0 | FULL_BOX_SIZE + self.urn.len() + 1 |
339 | 0 | } |
340 | | |
341 | 0 | pub fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
342 | 0 | let mut b = w.full_box(self.len(), *b"auxC", 0)?; |
343 | 0 | b.push(self.urn.as_bytes())?; |
344 | 0 | b.u8(0) |
345 | 0 | } |
346 | | } |
347 | | |
348 | | /// Pixies, I guess. |
349 | | #[derive(Debug, Copy, Clone)] |
350 | | pub struct PixiBox { |
351 | | pub depth: u8, |
352 | | pub channels: u8, |
353 | | } |
354 | | |
355 | | impl PixiBox { |
356 | 0 | pub fn len(self) -> usize { |
357 | 0 | FULL_BOX_SIZE |
358 | 0 | + 1 + self.channels as usize |
359 | 0 | } |
360 | | |
361 | 0 | pub fn write<B: WriterBackend>(self, w: &mut Writer<B>) -> Result<(), B::Error> { |
362 | 0 | let mut b = w.full_box(self.len(), *b"pixi", 0)?; |
363 | 0 | b.u8(self.channels)?; |
364 | 0 | for _ in 0..self.channels { |
365 | 0 | b.u8(self.depth)?; |
366 | | } |
367 | 0 | Ok(()) |
368 | 0 | } |
369 | | } |
370 | | |
371 | | /// This is HEVC-specific and not for AVIF, but Chrome wants it :( |
372 | | #[derive(Debug, Copy, Clone)] |
373 | | pub struct IspeBox { |
374 | | pub width: u32, |
375 | | pub height: u32, |
376 | | } |
377 | | |
378 | | impl MpegBox for IspeBox { |
379 | | #[inline(always)] |
380 | 0 | fn len(&self) -> usize { |
381 | 0 | FULL_BOX_SIZE + 4 + 4 |
382 | 0 | } |
383 | | |
384 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
385 | 0 | let mut b = w.full_box(self.len(), *b"ispe", 0)?; |
386 | 0 | b.u32(self.width)?; |
387 | 0 | b.u32(self.height) |
388 | 0 | } |
389 | | } |
390 | | |
391 | | /// Property→image associations |
392 | | #[derive(Debug, Clone)] |
393 | | pub struct IpmaEntry { |
394 | | pub item_id: u16, |
395 | | pub prop_ids: ArrayVec<u8, 7>, |
396 | | } |
397 | | |
398 | | #[derive(Debug, Clone)] |
399 | | pub struct IpmaBox { |
400 | | pub entries: ArrayVec<IpmaEntry, 2>, |
401 | | } |
402 | | |
403 | | impl MpegBox for IpmaBox { |
404 | | #[inline] |
405 | 0 | fn len(&self) -> usize { |
406 | 0 | FULL_BOX_SIZE + 4 + self.entries.iter().map(|e| 2 + 1 + e.prop_ids.len()).sum::<usize>() |
407 | 0 | } |
408 | | |
409 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
410 | 0 | let mut b = w.full_box(self.len(), *b"ipma", 0)?; |
411 | 0 | b.u32(self.entries.len() as _)?; // entry count |
412 | | |
413 | 0 | for e in &self.entries { |
414 | 0 | b.u16(e.item_id)?; |
415 | 0 | b.u8(e.prop_ids.len() as u8)?; // assoc count |
416 | 0 | for &p in &e.prop_ids { |
417 | 0 | b.u8(p)?; |
418 | | } |
419 | | } |
420 | 0 | Ok(()) |
421 | 0 | } |
422 | | } |
423 | | |
424 | | /// Item Reference box |
425 | | #[derive(Debug, Copy, Clone)] |
426 | | pub struct IrefEntryBox { |
427 | | pub from_id: u16, |
428 | | pub to_id: u16, |
429 | | pub typ: FourCC, |
430 | | } |
431 | | |
432 | | impl MpegBox for IrefEntryBox { |
433 | | #[inline(always)] |
434 | 0 | fn len(&self) -> usize { |
435 | 0 | BASIC_BOX_SIZE |
436 | 0 | + 2 // from |
437 | 0 | + 2 // refcount |
438 | 0 | + 2 // to |
439 | 0 | } |
440 | | |
441 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
442 | 0 | let mut b = w.basic_box(self.len(), self.typ.0)?; |
443 | 0 | b.u16(self.from_id)?; |
444 | 0 | b.u16(1)?; |
445 | 0 | b.u16(self.to_id) |
446 | 0 | } |
447 | | } |
448 | | |
449 | | #[derive(Debug, Clone)] |
450 | | pub struct IrefBox { |
451 | | pub entries: ArrayVec<IrefEntryBox, 3>, |
452 | | } |
453 | | |
454 | | impl IrefBox { |
455 | 0 | pub fn is_empty(&self) -> bool { |
456 | 0 | self.entries.is_empty() |
457 | 0 | } |
458 | | } |
459 | | |
460 | | impl MpegBox for IrefBox { |
461 | | #[inline(always)] |
462 | 0 | fn len(&self) -> usize { |
463 | 0 | FULL_BOX_SIZE + self.entries.iter().map(|e| e.len()).sum::<usize>() |
464 | 0 | } |
465 | | |
466 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
467 | 0 | let mut b = w.full_box(self.len(), *b"iref", 0)?; |
468 | 0 | for entry in &self.entries { |
469 | 0 | entry.write(&mut b)?; |
470 | | } |
471 | 0 | Ok(()) |
472 | 0 | } |
473 | | } |
474 | | |
475 | | /// Auxiliary item (alpha or depth map) |
476 | | #[derive(Debug, Copy, Clone)] |
477 | | #[allow(unused)] |
478 | | pub struct AuxlBox {} |
479 | | |
480 | | impl MpegBox for AuxlBox { |
481 | | #[inline(always)] |
482 | 0 | fn len(&self) -> usize { |
483 | 0 | FULL_BOX_SIZE |
484 | 0 | } |
485 | | |
486 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
487 | 0 | w.full_box(self.len(), *b"auxl", 0)?; |
488 | 0 | Ok(()) |
489 | 0 | } |
490 | | } |
491 | | |
492 | | /// ColourInformationBox |
493 | | #[derive(Debug, Copy, Clone, PartialEq)] |
494 | | pub struct ColrBox { |
495 | | pub color_primaries: ColorPrimaries, |
496 | | pub transfer_characteristics: TransferCharacteristics, |
497 | | pub matrix_coefficients: MatrixCoefficients, |
498 | | pub full_range_flag: bool, // u1 + u7 |
499 | | } |
500 | | |
501 | | impl Default for ColrBox { |
502 | 0 | fn default() -> Self { |
503 | 0 | Self { |
504 | 0 | color_primaries: ColorPrimaries::Bt709, |
505 | 0 | transfer_characteristics: TransferCharacteristics::Srgb, |
506 | 0 | matrix_coefficients: MatrixCoefficients::Bt601, |
507 | 0 | full_range_flag: true, |
508 | 0 | } |
509 | 0 | } |
510 | | } |
511 | | |
512 | | impl MpegBox for ColrBox { |
513 | | #[inline(always)] |
514 | 0 | fn len(&self) -> usize { |
515 | 0 | BASIC_BOX_SIZE + 4 + 2 + 2 + 2 + 1 |
516 | 0 | } |
517 | | |
518 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
519 | 0 | let mut b = w.basic_box(self.len(), *b"colr")?; |
520 | 0 | b.u32(u32::from_be_bytes(*b"nclx"))?; |
521 | 0 | b.u16(self.color_primaries as u16)?; |
522 | 0 | b.u16(self.transfer_characteristics as u16)?; |
523 | 0 | b.u16(self.matrix_coefficients as u16)?; |
524 | 0 | b.u8(if self.full_range_flag { 1 << 7 } else { 0 }) |
525 | 0 | } |
526 | | } |
527 | | |
528 | | /// Content Light Level Information box (`clli`), per ISOBMFF § 12.1.5 / CEA-861.3. |
529 | | /// |
530 | | /// Signals the content light level of HDR content to the display. |
531 | | /// Both values are in cd/m² (nits). |
532 | | #[derive(Debug, Copy, Clone, PartialEq)] |
533 | | pub struct ClliBox { |
534 | | /// Maximum light level of any single pixel in the content (MaxCLL). |
535 | | pub max_content_light_level: u16, |
536 | | /// Maximum average light level of any single frame in the content (MaxFALL). |
537 | | pub max_pic_average_light_level: u16, |
538 | | } |
539 | | |
540 | | impl MpegBox for ClliBox { |
541 | | #[inline(always)] |
542 | 0 | fn len(&self) -> usize { |
543 | 0 | BASIC_BOX_SIZE + 4 |
544 | 0 | } |
545 | | |
546 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
547 | 0 | let mut b = w.basic_box(self.len(), *b"clli")?; |
548 | 0 | b.u16(self.max_content_light_level)?; |
549 | 0 | b.u16(self.max_pic_average_light_level) |
550 | 0 | } |
551 | | } |
552 | | |
553 | | /// Mastering Display Colour Volume box (`mdcv`), per ISOBMFF § 12.1.5 / SMPTE ST 2086. |
554 | | /// |
555 | | /// Describes the color volume of the mastering display used to author the content. |
556 | | /// This does not describe the content itself — see [`ClliBox`] for that. |
557 | | #[derive(Debug, Copy, Clone, PartialEq)] |
558 | | pub struct MdcvBox { |
559 | | /// Display primaries in CIE 1931 xy chromaticity, encoded as the value × 50000. |
560 | | /// For example, D65 white (0.3127, 0.3290) encodes as (15635, 16450). |
561 | | /// Order: \[green, blue, red\] per SMPTE ST 2086. |
562 | | pub primaries: [(u16, u16); 3], |
563 | | /// White point in CIE 1931 xy chromaticity, same encoding as `primaries`. |
564 | | pub white_point: (u16, u16), |
565 | | /// Maximum luminance of the mastering display in cd/m² × 10000. |
566 | | /// For example, 1000 cd/m² = 10_000_000. |
567 | | pub max_luminance: u32, |
568 | | /// Minimum luminance of the mastering display in cd/m² × 10000. |
569 | | /// For example, 0.005 cd/m² = 50. |
570 | | pub min_luminance: u32, |
571 | | } |
572 | | |
573 | | impl MpegBox for MdcvBox { |
574 | | #[inline(always)] |
575 | 0 | fn len(&self) -> usize { |
576 | 0 | BASIC_BOX_SIZE + 24 |
577 | 0 | } |
578 | | |
579 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
580 | 0 | let mut b = w.basic_box(self.len(), *b"mdcv")?; |
581 | 0 | for &(x, y) in &self.primaries { |
582 | 0 | b.u16(x)?; |
583 | 0 | b.u16(y)?; |
584 | | } |
585 | 0 | b.u16(self.white_point.0)?; |
586 | 0 | b.u16(self.white_point.1)?; |
587 | 0 | b.u32(self.max_luminance)?; |
588 | 0 | b.u32(self.min_luminance) |
589 | 0 | } |
590 | | } |
591 | | |
592 | | #[derive(Debug, Copy, Clone)] |
593 | | pub struct Av1CBox { |
594 | | pub seq_profile: u8, |
595 | | pub seq_level_idx_0: u8, |
596 | | pub seq_tier_0: bool, |
597 | | pub high_bitdepth: bool, |
598 | | pub twelve_bit: bool, |
599 | | pub monochrome: bool, |
600 | | pub chroma_subsampling_x: bool, |
601 | | pub chroma_subsampling_y: bool, |
602 | | pub chroma_sample_position: u8, |
603 | | } |
604 | | |
605 | | impl MpegBox for Av1CBox { |
606 | | #[inline(always)] |
607 | 0 | fn len(&self) -> usize { |
608 | 0 | BASIC_BOX_SIZE + 4 |
609 | 0 | } |
610 | | |
611 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
612 | 0 | let mut b = w.basic_box(self.len(), *b"av1C")?; |
613 | 0 | let flags1 = |
614 | 0 | u8::from(self.seq_tier_0) << 7 | |
615 | 0 | u8::from(self.high_bitdepth) << 6 | |
616 | 0 | u8::from(self.twelve_bit) << 5 | |
617 | 0 | u8::from(self.monochrome) << 4 | |
618 | 0 | u8::from(self.chroma_subsampling_x) << 3 | |
619 | 0 | u8::from(self.chroma_subsampling_y) << 2 | |
620 | 0 | self.chroma_sample_position; |
621 | | |
622 | 0 | b.push(&[ |
623 | 0 | 0x81, // marker and version |
624 | 0 | (self.seq_profile << 5) | self.seq_level_idx_0, // x2d == 45 |
625 | 0 | flags1, |
626 | 0 | 0, |
627 | 0 | ]) |
628 | 0 | } |
629 | | } |
630 | | |
631 | | #[derive(Debug, Copy, Clone)] |
632 | | pub struct PitmBox(pub u16); |
633 | | |
634 | | impl MpegBox for PitmBox { |
635 | | #[inline(always)] |
636 | 0 | fn len(&self) -> usize { |
637 | 0 | FULL_BOX_SIZE + 2 |
638 | 0 | } |
639 | | |
640 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
641 | 0 | let mut b = w.full_box(self.len(), *b"pitm", 0)?; |
642 | 0 | b.u16(self.0) |
643 | 0 | } |
644 | | } |
645 | | |
646 | | #[derive(Debug, Clone)] |
647 | | pub struct IlocBox<'data> { |
648 | | /// update before writing |
649 | | pub absolute_offset_start: Option<NonZeroU32>, |
650 | | pub items: ArrayVec<IlocItem<'data>, 3>, |
651 | | } |
652 | | |
653 | | #[derive(Debug, Clone)] |
654 | | pub struct IlocItem<'data> { |
655 | | pub id: u16, |
656 | | pub extents: [IlocExtent<'data>; 1], |
657 | | } |
658 | | |
659 | | #[derive(Debug, Copy, Clone)] |
660 | | pub struct IlocExtent<'data> { |
661 | | /// offset and len will be calculated when writing |
662 | | pub data: &'data [u8], |
663 | | } |
664 | | |
665 | | impl MpegBox for IlocBox<'_> { |
666 | | #[inline(always)] |
667 | | #[allow(unused_parens)] |
668 | 0 | fn len(&self) -> usize { |
669 | 0 | FULL_BOX_SIZE |
670 | 0 | + 1 // offset_size, length_size |
671 | 0 | + 1 // base_offset_size, reserved |
672 | 0 | + 2 // num items |
673 | 0 | + self.items.iter().map(|i| ( // for each item |
674 | 0 | 2 // id |
675 | 0 | + 2 // dat ref idx |
676 | 0 | + 0 // base_offset_size |
677 | 0 | + 2 // extent count |
678 | 0 | + i.extents.len() * ( // for each extent |
679 | 0 | 4 // extent_offset |
680 | 0 | + 4 // extent_len |
681 | 0 | ) |
682 | 0 | )).sum::<usize>() |
683 | 0 | } |
684 | | |
685 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
686 | 0 | let mut b = w.full_box(self.len(), *b"iloc", 0)?; |
687 | 0 | b.push(&[4 << 4 | 4, 0])?; // offset and length are 4 bytes |
688 | | |
689 | 0 | b.u16(self.items.len() as _)?; // num items |
690 | 0 | let mut next_start = if let Some(ok) = self.absolute_offset_start { ok.get() } else { |
691 | 0 | debug_assert!(false); |
692 | 0 | !0 |
693 | | }; |
694 | 0 | for item in &self.items { |
695 | 0 | b.u16(item.id)?; |
696 | 0 | b.u16(0)?; |
697 | 0 | b.u16(item.extents.len() as _)?; // num extents |
698 | 0 | for ex in &item.extents { |
699 | 0 | let len = ex.data.len() as u32; |
700 | 0 | b.u32(next_start)?; |
701 | 0 | next_start += len; |
702 | 0 | b.u32(len)?; |
703 | | } |
704 | | } |
705 | 0 | Ok(()) |
706 | 0 | } |
707 | | } |
708 | | |
709 | | #[derive(Debug, Clone)] |
710 | | pub struct MdatBox; |
711 | | |
712 | | impl MdatBox { |
713 | | #[inline(always)] |
714 | 0 | fn len(&self, chunks: &IlocBox) -> usize { |
715 | 0 | BASIC_BOX_SIZE + chunks.items.iter().flat_map(|c| &c.extents).map(|d| d.data.len()).sum::<usize>() |
716 | 0 | } |
717 | | |
718 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>, chunks: &IlocBox) -> Result<(), B::Error> { |
719 | 0 | let mut b = w.basic_box(self.len(chunks), *b"mdat")?; |
720 | 0 | for ch in chunks.items.iter().flat_map(|c| &c.extents) { |
721 | 0 | b.push(ch.data)?; |
722 | | } |
723 | 0 | Ok(()) |
724 | 0 | } |
725 | | } |