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