/rust/registry/src/index.crates.io-1949cf8c6b5b557f/avif-serialize-0.8.6/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().map_or(true, |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 | | } |
261 | | |
262 | | impl IpcoProp { |
263 | 0 | pub fn len(&self) -> usize { |
264 | 0 | match self { |
265 | 0 | Self::Av1C(p) => p.len(), |
266 | 0 | Self::Pixi(p) => p.len(), |
267 | 0 | Self::Ispe(p) => p.len(), |
268 | 0 | Self::AuxC(p) => p.len(), |
269 | 0 | Self::Colr(p) => p.len(), |
270 | | } |
271 | 0 | } |
272 | | |
273 | 0 | pub fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
274 | 0 | match self { |
275 | 0 | Self::Av1C(p) => p.write(w), |
276 | 0 | Self::Pixi(p) => p.write(w), |
277 | 0 | Self::Ispe(p) => p.write(w), |
278 | 0 | Self::AuxC(p) => p.write(w), |
279 | 0 | Self::Colr(p) => p.write(w), |
280 | | } |
281 | 0 | } |
282 | | } |
283 | | |
284 | | /// Item Property Container box |
285 | | #[derive(Debug, Clone)] |
286 | | pub struct IpcoBox { |
287 | | props: ArrayVec<IpcoProp, 7>, |
288 | | } |
289 | | |
290 | | impl IpcoBox { |
291 | 0 | pub fn new() -> Self { |
292 | 0 | Self { props: ArrayVec::new() } |
293 | 0 | } |
294 | | |
295 | | #[must_use] |
296 | 0 | pub fn push(&mut self, prop: IpcoProp) -> Option<u8> { |
297 | 0 | self.props.try_push(prop).ok()?; |
298 | 0 | Some(self.props.len() as u8) // the spec wants them off by one |
299 | 0 | } |
300 | | |
301 | 0 | pub(crate) fn ispe(&self) -> Option<&IspeBox> { |
302 | 0 | self.props.iter().find_map(|b| match b { |
303 | 0 | IpcoProp::Ispe(i) => Some(i), |
304 | 0 | _ => None, |
305 | 0 | }) |
306 | 0 | } |
307 | | } |
308 | | |
309 | | impl MpegBox for IpcoBox { |
310 | | #[inline] |
311 | 0 | fn len(&self) -> usize { |
312 | | BASIC_BOX_SIZE |
313 | 0 | + self.props.iter().map(|a| a.len()).sum::<usize>() |
314 | 0 | } |
315 | | |
316 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
317 | 0 | let mut b = w.basic_box(self.len(), *b"ipco")?; |
318 | 0 | for p in &self.props { |
319 | 0 | p.write(&mut b)?; |
320 | | } |
321 | 0 | Ok(()) |
322 | 0 | } |
323 | | } |
324 | | |
325 | | #[derive(Debug, Copy, Clone)] |
326 | | pub struct AuxCBox { |
327 | | pub urn: &'static str, |
328 | | } |
329 | | |
330 | | impl AuxCBox { |
331 | 0 | pub fn len(&self) -> usize { |
332 | 0 | FULL_BOX_SIZE + self.urn.len() + 1 |
333 | 0 | } |
334 | | |
335 | 0 | pub fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
336 | 0 | let mut b = w.full_box(self.len(), *b"auxC", 0)?; |
337 | 0 | b.push(self.urn.as_bytes())?; |
338 | 0 | b.u8(0) |
339 | 0 | } |
340 | | } |
341 | | |
342 | | /// Pixies, I guess. |
343 | | #[derive(Debug, Copy, Clone)] |
344 | | pub struct PixiBox { |
345 | | pub depth: u8, |
346 | | pub channels: u8, |
347 | | } |
348 | | |
349 | | impl PixiBox { |
350 | 0 | pub fn len(self) -> usize { |
351 | 0 | FULL_BOX_SIZE |
352 | 0 | + 1 + self.channels as usize |
353 | 0 | } |
354 | | |
355 | 0 | pub fn write<B: WriterBackend>(self, w: &mut Writer<B>) -> Result<(), B::Error> { |
356 | 0 | let mut b = w.full_box(self.len(), *b"pixi", 0)?; |
357 | 0 | b.u8(self.channels)?; |
358 | 0 | for _ in 0..self.channels { |
359 | 0 | b.u8(self.depth)?; |
360 | | } |
361 | 0 | Ok(()) |
362 | 0 | } |
363 | | } |
364 | | |
365 | | /// This is HEVC-specific and not for AVIF, but Chrome wants it :( |
366 | | #[derive(Debug, Copy, Clone)] |
367 | | pub struct IspeBox { |
368 | | pub width: u32, |
369 | | pub height: u32, |
370 | | } |
371 | | |
372 | | impl MpegBox for IspeBox { |
373 | | #[inline(always)] |
374 | 0 | fn len(&self) -> usize { |
375 | 0 | FULL_BOX_SIZE + 4 + 4 |
376 | 0 | } |
377 | | |
378 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
379 | 0 | let mut b = w.full_box(self.len(), *b"ispe", 0)?; |
380 | 0 | b.u32(self.width)?; |
381 | 0 | b.u32(self.height) |
382 | 0 | } |
383 | | } |
384 | | |
385 | | /// Property→image associations |
386 | | #[derive(Debug, Clone)] |
387 | | pub struct IpmaEntry { |
388 | | pub item_id: u16, |
389 | | pub prop_ids: ArrayVec<u8, 5>, |
390 | | } |
391 | | |
392 | | #[derive(Debug, Clone)] |
393 | | pub struct IpmaBox { |
394 | | pub entries: ArrayVec<IpmaEntry, 2>, |
395 | | } |
396 | | |
397 | | impl MpegBox for IpmaBox { |
398 | | #[inline] |
399 | 0 | fn len(&self) -> usize { |
400 | 0 | FULL_BOX_SIZE + 4 + self.entries.iter().map(|e| 2 + 1 + e.prop_ids.len()).sum::<usize>() |
401 | 0 | } |
402 | | |
403 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
404 | 0 | let mut b = w.full_box(self.len(), *b"ipma", 0)?; |
405 | 0 | b.u32(self.entries.len() as _)?; // entry count |
406 | | |
407 | 0 | for e in &self.entries { |
408 | 0 | b.u16(e.item_id)?; |
409 | 0 | b.u8(e.prop_ids.len() as u8)?; // assoc count |
410 | 0 | for &p in &e.prop_ids { |
411 | 0 | b.u8(p)?; |
412 | | } |
413 | | } |
414 | 0 | Ok(()) |
415 | 0 | } |
416 | | } |
417 | | |
418 | | /// Item Reference box |
419 | | #[derive(Debug, Copy, Clone)] |
420 | | pub struct IrefEntryBox { |
421 | | pub from_id: u16, |
422 | | pub to_id: u16, |
423 | | pub typ: FourCC, |
424 | | } |
425 | | |
426 | | impl MpegBox for IrefEntryBox { |
427 | | #[inline(always)] |
428 | 0 | fn len(&self) -> usize { |
429 | 0 | BASIC_BOX_SIZE |
430 | 0 | + 2 // from |
431 | 0 | + 2 // refcount |
432 | 0 | + 2 // to |
433 | 0 | } |
434 | | |
435 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
436 | 0 | let mut b = w.basic_box(self.len(), self.typ.0)?; |
437 | 0 | b.u16(self.from_id)?; |
438 | 0 | b.u16(1)?; |
439 | 0 | b.u16(self.to_id) |
440 | 0 | } |
441 | | } |
442 | | |
443 | | #[derive(Debug, Clone)] |
444 | | pub struct IrefBox { |
445 | | pub entries: ArrayVec<IrefEntryBox, 3>, |
446 | | } |
447 | | |
448 | | impl IrefBox { |
449 | 0 | pub fn is_empty(&self) -> bool { |
450 | 0 | self.entries.is_empty() |
451 | 0 | } |
452 | | } |
453 | | |
454 | | impl MpegBox for IrefBox { |
455 | | #[inline(always)] |
456 | 0 | fn len(&self) -> usize { |
457 | 0 | FULL_BOX_SIZE + self.entries.iter().map(|e| e.len()).sum::<usize>() |
458 | 0 | } |
459 | | |
460 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
461 | 0 | let mut b = w.full_box(self.len(), *b"iref", 0)?; |
462 | 0 | for entry in &self.entries { |
463 | 0 | entry.write(&mut b)?; |
464 | | } |
465 | 0 | Ok(()) |
466 | 0 | } |
467 | | } |
468 | | |
469 | | /// Auxiliary item (alpha or depth map) |
470 | | #[derive(Debug, Copy, Clone)] |
471 | | #[allow(unused)] |
472 | | pub struct AuxlBox {} |
473 | | |
474 | | impl MpegBox for AuxlBox { |
475 | | #[inline(always)] |
476 | 0 | fn len(&self) -> usize { |
477 | 0 | FULL_BOX_SIZE |
478 | 0 | } |
479 | | |
480 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
481 | 0 | w.full_box(self.len(), *b"auxl", 0)?; |
482 | 0 | Ok(()) |
483 | 0 | } |
484 | | } |
485 | | |
486 | | /// ColourInformationBox |
487 | | #[derive(Debug, Copy, Clone, PartialEq)] |
488 | | pub struct ColrBox { |
489 | | pub color_primaries: ColorPrimaries, |
490 | | pub transfer_characteristics: TransferCharacteristics, |
491 | | pub matrix_coefficients: MatrixCoefficients, |
492 | | pub full_range_flag: bool, // u1 + u7 |
493 | | } |
494 | | |
495 | | impl Default for ColrBox { |
496 | 0 | fn default() -> Self { |
497 | 0 | Self { |
498 | 0 | color_primaries: ColorPrimaries::Bt709, |
499 | 0 | transfer_characteristics: TransferCharacteristics::Srgb, |
500 | 0 | matrix_coefficients: MatrixCoefficients::Bt601, |
501 | 0 | full_range_flag: true, |
502 | 0 | } |
503 | 0 | } |
504 | | } |
505 | | |
506 | | impl MpegBox for ColrBox { |
507 | | #[inline(always)] |
508 | 0 | fn len(&self) -> usize { |
509 | 0 | BASIC_BOX_SIZE + 4 + 2 + 2 + 2 + 1 |
510 | 0 | } |
511 | | |
512 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
513 | 0 | let mut b = w.basic_box(self.len(), *b"colr")?; |
514 | 0 | b.u32(u32::from_be_bytes(*b"nclx"))?; |
515 | 0 | b.u16(self.color_primaries as u16)?; |
516 | 0 | b.u16(self.transfer_characteristics as u16)?; |
517 | 0 | b.u16(self.matrix_coefficients as u16)?; |
518 | 0 | b.u8(if self.full_range_flag { 1 << 7 } else { 0 }) |
519 | 0 | } |
520 | | } |
521 | | #[derive(Debug, Copy, Clone)] |
522 | | pub 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 | | |
534 | | impl MpegBox for Av1CBox { |
535 | | #[inline(always)] |
536 | 0 | fn len(&self) -> usize { |
537 | 0 | BASIC_BOX_SIZE + 4 |
538 | 0 | } |
539 | | |
540 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
541 | 0 | let mut b = w.basic_box(self.len(), *b"av1C")?; |
542 | 0 | let flags1 = |
543 | 0 | u8::from(self.seq_tier_0) << 7 | |
544 | 0 | u8::from(self.high_bitdepth) << 6 | |
545 | 0 | u8::from(self.twelve_bit) << 5 | |
546 | 0 | u8::from(self.monochrome) << 4 | |
547 | 0 | u8::from(self.chroma_subsampling_x) << 3 | |
548 | 0 | u8::from(self.chroma_subsampling_y) << 2 | |
549 | 0 | self.chroma_sample_position; |
550 | | |
551 | 0 | b.push(&[ |
552 | 0 | 0x81, // marker and version |
553 | 0 | (self.seq_profile << 5) | self.seq_level_idx_0, // x2d == 45 |
554 | 0 | flags1, |
555 | 0 | 0, |
556 | 0 | ]) |
557 | 0 | } |
558 | | } |
559 | | |
560 | | #[derive(Debug, Copy, Clone)] |
561 | | pub struct PitmBox(pub u16); |
562 | | |
563 | | impl MpegBox for PitmBox { |
564 | | #[inline(always)] |
565 | 0 | fn len(&self) -> usize { |
566 | 0 | FULL_BOX_SIZE + 2 |
567 | 0 | } |
568 | | |
569 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
570 | 0 | let mut b = w.full_box(self.len(), *b"pitm", 0)?; |
571 | 0 | b.u16(self.0) |
572 | 0 | } |
573 | | } |
574 | | |
575 | | #[derive(Debug, Clone)] |
576 | | pub struct IlocBox<'data> { |
577 | | /// update before writing |
578 | | pub absolute_offset_start: Option<NonZeroU32>, |
579 | | pub items: ArrayVec<IlocItem<'data>, 3>, |
580 | | } |
581 | | |
582 | | #[derive(Debug, Clone)] |
583 | | pub struct IlocItem<'data> { |
584 | | pub id: u16, |
585 | | pub extents: [IlocExtent<'data>; 1], |
586 | | } |
587 | | |
588 | | #[derive(Debug, Copy, Clone)] |
589 | | pub struct IlocExtent<'data> { |
590 | | /// offset and len will be calculated when writing |
591 | | pub data: &'data [u8], |
592 | | } |
593 | | |
594 | | impl MpegBox for IlocBox<'_> { |
595 | | #[inline(always)] |
596 | | #[allow(unused_parens)] |
597 | 0 | fn len(&self) -> usize { |
598 | 0 | FULL_BOX_SIZE |
599 | 0 | + 1 // offset_size, length_size |
600 | 0 | + 1 // base_offset_size, reserved |
601 | 0 | + 2 // num items |
602 | 0 | + self.items.iter().map(|i| ( // for each item |
603 | 0 | 2 // id |
604 | 0 | + 2 // dat ref idx |
605 | 0 | + 0 // base_offset_size |
606 | 0 | + 2 // extent count |
607 | 0 | + i.extents.len() * ( // for each extent |
608 | 0 | 4 // extent_offset |
609 | 0 | + 4 // extent_len |
610 | 0 | ) |
611 | 0 | )).sum::<usize>() |
612 | 0 | } |
613 | | |
614 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>) -> Result<(), B::Error> { |
615 | 0 | let mut b = w.full_box(self.len(), *b"iloc", 0)?; |
616 | 0 | b.push(&[4 << 4 | 4, 0])?; // offset and length are 4 bytes |
617 | | |
618 | 0 | b.u16(self.items.len() as _)?; // num items |
619 | 0 | let mut next_start = if let Some(ok) = self.absolute_offset_start { ok.get() } else { |
620 | 0 | debug_assert!(false); |
621 | 0 | !0 |
622 | | }; |
623 | 0 | for item in &self.items { |
624 | 0 | b.u16(item.id)?; |
625 | 0 | b.u16(0)?; |
626 | 0 | b.u16(item.extents.len() as _)?; // num extents |
627 | 0 | for ex in &item.extents { |
628 | 0 | let len = ex.data.len() as u32; |
629 | 0 | b.u32(next_start)?; |
630 | 0 | next_start += len; |
631 | 0 | b.u32(len)?; |
632 | | } |
633 | | } |
634 | 0 | Ok(()) |
635 | 0 | } |
636 | | } |
637 | | |
638 | | #[derive(Debug, Clone)] |
639 | | pub struct MdatBox; |
640 | | |
641 | | impl MdatBox { |
642 | | #[inline(always)] |
643 | 0 | fn len(&self, chunks: &IlocBox) -> usize { |
644 | 0 | BASIC_BOX_SIZE + chunks.items.iter().flat_map(|c| &c.extents).map(|d| d.data.len()).sum::<usize>() |
645 | 0 | } |
646 | | |
647 | 0 | fn write<B: WriterBackend>(&self, w: &mut Writer<B>, chunks: &IlocBox) -> Result<(), B::Error> { |
648 | 0 | let mut b = w.basic_box(self.len(chunks), *b"mdat")?; |
649 | 0 | for ch in chunks.items.iter().flat_map(|c| &c.extents) { |
650 | 0 | b.push(ch.data)?; |
651 | | } |
652 | 0 | Ok(()) |
653 | 0 | } |
654 | | } |