| 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 | |