Vendor rawler 0.7.2 unmodified

The crate as crates.io publishes it, minus .cargo-ok, its Cargo.lock and
data/testdata (13 MB of sample files only its own tests read). Not yet
routed through [patch.crates-io]; the next commit is the patch.
This commit is contained in:
2026-09-27 17:33:19 -04:00
parent 0d9feb0556
commit 77a1925bac
940 changed files with 66954 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
use super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Co64Box {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub entries: Vec<u64>,
}
impl Co64Box {
pub const TYP: FourCC = FourCC::with(['c', 'o', '6', '4']);
}
impl<R: Read + Seek> ReadBox<&mut R> for Co64Box {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
let entry_count = reader.read_u32::<BigEndian>()?;
let mut entries = Vec::with_capacity(entry_count as usize);
for _i in 0..entry_count {
let chunk_offset = reader.read_u64::<BigEndian>()?;
entries.push(chunk_offset);
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
entries,
})
}
}
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct DinfBox {
pub header: BoxHeader,
}
impl DinfBox {
pub const TYP: FourCC = FourCC::with(['d', 'i', 'n', 'f']);
}
impl<R: Read + Seek> ReadBox<&mut R> for DinfBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header })
}
}
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct CcdtBox {
pub header: BoxHeader,
pub image_type: u64,
pub dual_pixel: u32,
pub trak_index: u32,
}
impl CcdtBox {
pub const TYP: FourCC = FourCC::with(['C', 'C', 'D', 'T']);
}
impl<R: Read + Seek> ReadBox<&mut R> for CcdtBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let image_type = reader.read_u64::<BigEndian>()?;
let dual_pixel = reader.read_u32::<BigEndian>()?;
let trak_index = reader.read_u32::<BigEndian>()?;
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
image_type,
dual_pixel,
trak_index,
})
}
}
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use byteorder::{BigEndian, ReadBytesExt};
use log::debug;
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
use super::{
super::{BmffError, BoxHeader, FourCC, ReadBox, Result, read_box_header_ext},
ccdt::CcdtBox,
};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct CctpBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub unknown1: u32,
pub lines: u32,
pub ccdts: Vec<CcdtBox>,
}
impl CctpBox {
pub const TYP: FourCC = FourCC::with(['C', 'C', 'T', 'P']);
}
impl<R: Read + Seek> ReadBox<&mut R> for CctpBox {
fn read_box(mut reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
let unknown1 = reader.read_u32::<BigEndian>()?;
let lines = reader.read_u32::<BigEndian>()?;
let mut ccdts = Vec::new();
let mut current = reader.stream_position()?;
while current < header.end_offset() {
// get box?
let header = BoxHeader::parse(&mut reader)?;
match header.typ {
CcdtBox::TYP => {
let ccdt = CcdtBox::read_box(&mut reader, header)?;
ccdts.push(ccdt);
}
_ => {
debug!("Vendor box found in CCTP: {:?}", header.typ);
return Err(BmffError::Parse(format!("Invalid box found in CCTP: {:?}", header.typ)));
}
}
current = reader.stream_position()?;
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
unknown1,
lines,
ccdts,
})
}
}
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{
super::{BmffError, BoxHeader, FourCC, ReadBox, Result, read_box_header_ext},
iad1::Iad1Box,
};
use log::debug;
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Cdi1Box {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub iad1: Iad1Box,
}
impl Cdi1Box {
pub const TYP: FourCC = FourCC::with(['C', 'D', 'I', '1']);
}
impl<R: Read + Seek> ReadBox<&mut R> for Cdi1Box {
fn read_box(mut reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
let mut iad1 = None;
let mut current = reader.stream_position()?;
while current < header.end_offset() {
// get box?
let header = BoxHeader::parse(&mut reader)?;
match header.typ {
Iad1Box::TYP => {
iad1 = Some(Iad1Box::read_box(&mut reader, header)?);
}
_ => {
debug!("Vendor box found in CDI1: {:?}", header.typ);
return Err(BmffError::Parse(format!("Invalid box found in CDI1: {:?}", header.typ)));
}
}
current = reader.stream_position()?;
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
iad1: iad1.ok_or_else(|| BmffError::Parse("IAD1 box not found, corrupt file?".into()))?,
})
}
}
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Cmp1Box {
pub header: BoxHeader,
pub unknown1: i16,
pub header_size: u16,
pub version: u16,
pub version_sub: u16,
pub f_width: u32,
pub f_height: u32,
pub tile_width: u32,
pub tile_height: u32,
pub n_bits: u8,
pub n_planes: u8,
pub cfa_layout: u8,
pub enc_type: u8,
pub image_levels: u8,
pub has_tile_cols: u8,
pub has_tile_rows: u8,
pub mdat_hdr_size: u32,
pub ext_header: u32,
pub median_bits: u8,
// TODO: missing fields
}
impl Cmp1Box {
pub const TYP: FourCC = FourCC::with(['C', 'M', 'P', '1']);
}
impl<R: Read + Seek> ReadBox<&mut R> for Cmp1Box {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let unknown1 = reader.read_i16::<BigEndian>()?;
let header_size = reader.read_u16::<BigEndian>()?;
let version = reader.read_u16::<BigEndian>()?;
let version_sub = reader.read_u16::<BigEndian>()?;
let f_width = reader.read_u32::<BigEndian>()?;
let f_height = reader.read_u32::<BigEndian>()?;
let tile_width = reader.read_u32::<BigEndian>()?;
let tile_height = reader.read_u32::<BigEndian>()?;
let n_bits = reader.read_u8()?;
let (n_planes, cfa_layout) = {
let tmp = reader.read_u8()?;
(tmp >> 4, tmp & 0xF)
};
let (enc_type, image_levels) = {
let tmp = reader.read_u8()?;
(tmp >> 4, tmp & 0xF)
};
let (has_tile_cols, has_tile_rows) = {
let tmp = reader.read_u8()?;
(tmp >> 7, tmp & 1)
};
let mdat_hdr_size = reader.read_u32::<BigEndian>()?;
let ext_header = reader.read_u32::<BigEndian>()?; // 32
// Median bit precision for enc_type 3 is not always same as
// image bit precision. For CRM movie files, there is an extended header.
let mut median_bits = n_bits;
// CRM Movie file
if (ext_header & 0x80000000 != 0) && n_planes == 4 && header.size >= 56 {
let _unknow = reader.read_u32::<BigEndian>()?; // 36
let _unknow = reader.read_u32::<BigEndian>()?; // 40
let _unknow = reader.read_u32::<BigEndian>()?; // 44
let _unknow = reader.read_u32::<BigEndian>()?; // 48
let _unknow = reader.read_u32::<BigEndian>()?; // 52
let use_median_bits = reader.read_u32::<BigEndian>()? & 0x40000000 != 0; // 56
if use_median_bits && header.size > 84 {
let _unknow = reader.read_u32::<BigEndian>()?; // 60
let _unknow = reader.read_u32::<BigEndian>()?; // 64
let _unknow = reader.read_u32::<BigEndian>()?; // 68
let _unknow = reader.read_u32::<BigEndian>()?; // 72
let _unknow = reader.read_u32::<BigEndian>()?; // 76
let _unknow = reader.read_u32::<BigEndian>()?; // 80
median_bits = reader.read_u8()?; // 84
}
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
unknown1,
header_size,
version,
version_sub,
f_width,
f_height,
tile_width,
tile_height,
n_bits,
n_planes,
cfa_layout,
enc_type,
image_levels,
has_tile_cols,
has_tile_rows,
mdat_hdr_size,
ext_header,
median_bits,
})
}
}
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use crate::{formats::bmff::BmffError, formats::tiff::GenericTiffReader};
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Cmt1Box {
pub header: BoxHeader,
pub data: Vec<u8>,
pub tiff: GenericTiffReader,
}
impl Cmt1Box {
pub const TYP: FourCC = FourCC::with(['C', 'M', 'T', '1']);
}
impl<R: Read + Seek> ReadBox<&mut R> for Cmt1Box {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let current = reader.stream_position()?;
let data_len = header.end_offset() - current;
let mut data = vec![0; data_len as usize];
reader.read_exact(&mut data)?;
reader.seek(SeekFrom::Start(header.end_offset()))?;
let tiff = GenericTiffReader::new_with_buffer(&data, 0, 0, None).map_err(|e| BmffError::Parse(e.to_string()))?;
Ok(Self { header, data, tiff })
}
}
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use crate::{formats::bmff::BmffError, formats::tiff::GenericTiffReader};
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Cmt2Box {
pub header: BoxHeader,
pub data: Vec<u8>,
pub tiff: GenericTiffReader,
}
impl Cmt2Box {
pub const TYP: FourCC = FourCC::with(['C', 'M', 'T', '2']);
}
impl<R: Read + Seek> ReadBox<&mut R> for Cmt2Box {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let current = reader.stream_position()?;
let data_len = header.end_offset() - current;
let mut data = vec![0; data_len as usize];
reader.read_exact(&mut data)?;
reader.seek(SeekFrom::Start(header.end_offset()))?;
let tiff = GenericTiffReader::new_with_buffer(&data, 0, 0, None).map_err(|e| BmffError::Parse(e.to_string()))?;
Ok(Self { header, data, tiff })
}
}
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use crate::{formats::bmff::BmffError, formats::tiff::GenericTiffReader};
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Cmt3Box {
pub header: BoxHeader,
pub data: Vec<u8>,
pub tiff: GenericTiffReader,
}
impl Cmt3Box {
pub const TYP: FourCC = FourCC::with(['C', 'M', 'T', '3']);
}
impl<R: Read + Seek> ReadBox<&mut R> for Cmt3Box {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let current = reader.stream_position()?;
let data_len = header.end_offset() - current;
let mut data = vec![0; data_len as usize];
reader.read_exact(&mut data)?;
reader.seek(SeekFrom::Start(header.end_offset()))?;
let tiff = GenericTiffReader::new_with_buffer(&data, 0, 0, None).map_err(|e| BmffError::Parse(e.to_string()))?;
Ok(Self { header, data, tiff })
}
}
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use crate::{formats::bmff::BmffError, formats::tiff::GenericTiffReader};
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Cmt4Box {
pub header: BoxHeader,
pub data: Vec<u8>,
pub tiff: GenericTiffReader,
}
impl Cmt4Box {
pub const TYP: FourCC = FourCC::with(['C', 'M', 'T', '4']);
}
impl<R: Read + Seek> ReadBox<&mut R> for Cmt4Box {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let current = reader.stream_position()?;
let data_len = header.end_offset() - current;
let mut data = vec![0; data_len as usize];
reader.read_exact(&mut data)?;
reader.seek(SeekFrom::Start(header.end_offset()))?;
let tiff = GenericTiffReader::new_with_buffer(&data, 0, 0, None).map_err(|e| BmffError::Parse(e.to_string()))?;
Ok(Self { header, data, tiff })
}
}
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct CncvBox {
pub header: BoxHeader,
pub compressor: String,
}
impl CncvBox {
pub const TYP: FourCC = FourCC::with(['C', 'N', 'C', 'V']);
}
impl<R: Read + Seek> ReadBox<&mut R> for CncvBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let mut buf = [0_u8; 30];
reader.read_exact(&mut buf)?;
let compressor = String::from_utf8_lossy(&buf);
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
compressor: compressor.into(),
})
}
}
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CnopBox {
pub header: BoxHeader,
pub data: Vec<u8>,
}
impl CnopBox {
pub const TYP: FourCC = FourCC::with(['C', 'N', 'O', 'P']);
}
impl<R: Read + Seek> ReadBox<&mut R> for CnopBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let current = reader.stream_position()?;
let data_len = header.end_offset() - current;
let mut data = vec![0; data_len as usize];
reader.read_exact(&mut data)?;
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header, data })
}
}
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{
super::{BmffError, BoxHeader, ReadBox, Result},
cctp::CctpBox,
cmt1::Cmt1Box,
cmt2::Cmt2Box,
cmt3::Cmt3Box,
cmt4::Cmt4Box,
cncv::CncvBox,
cnop::CnopBox,
ctbo::CtboBox,
thmb::ThmbBox,
};
use crate::formats::bmff::{free::FreeBox, skip::SkipBox};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Cr3DescBox {
pub header: BoxHeader,
pub cncv: CncvBox,
pub cctp: CctpBox,
pub ctbo: CtboBox,
pub cmt1: Cmt1Box,
pub cmt2: Cmt2Box,
pub cmt3: Cmt3Box,
pub cmt4: Cmt4Box,
pub thmb: ThmbBox,
}
impl Cr3DescBox {
//pub const TYP: FourCC = FourCC::with(['u', 'u', 'i', 'd']);
pub const UUID: [u8; 16] = [0x85, 0xc0, 0xb6, 0x87, 0x82, 0x0f, 0x11, 0xe0, 0x81, 0x11, 0xf4, 0xce, 0x46, 0x2b, 0x6a, 0x48];
}
impl<R: Read + Seek> ReadBox<&mut R> for Cr3DescBox {
fn read_box(mut reader: &mut R, header: BoxHeader) -> Result<Self> {
let mut cncv = None;
let mut cctp = None;
let mut ctbo = None;
let mut cmt1 = None;
let mut cmt2 = None;
let mut cmt3 = None;
let mut cmt4 = None;
let mut thmb = None;
let mut current = reader.stream_position()?;
while current < header.end_offset() {
// get box?
let header = BoxHeader::parse(&mut reader)?;
match header.typ {
CncvBox::TYP => {
cncv = Some(CncvBox::read_box(&mut reader, header)?);
}
CctpBox::TYP => {
cctp = Some(CctpBox::read_box(&mut reader, header)?);
}
CtboBox::TYP => {
ctbo = Some(CtboBox::read_box(&mut reader, header)?);
}
Cmt1Box::TYP => {
cmt1 = Some(Cmt1Box::read_box(&mut reader, header)?);
}
Cmt2Box::TYP => {
cmt2 = Some(Cmt2Box::read_box(&mut reader, header)?);
}
Cmt3Box::TYP => {
cmt3 = Some(Cmt3Box::read_box(&mut reader, header)?);
}
Cmt4Box::TYP => {
cmt4 = Some(Cmt4Box::read_box(&mut reader, header)?);
}
CnopBox::TYP => {
let _ignore = Some(CnopBox::read_box(&mut reader, header)?);
}
ThmbBox::TYP => {
thmb = Some(ThmbBox::read_box(&mut reader, header)?);
}
SkipBox::TYP => {
let _ignore = SkipBox::read_box(&mut reader, header)?;
}
FreeBox::TYP => {
let _ignore = FreeBox::read_box(&mut reader, header)?;
}
_ => return Err(BmffError::Parse(format!("Unknown box in Cr3desc: {}", header.typ))),
}
current = reader.stream_position()?;
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
cncv: cncv.ok_or_else(|| BmffError::Parse("cncv box not found, corrupt file?".into()))?,
cctp: cctp.ok_or_else(|| BmffError::Parse("cctp box not found, corrupt file?".into()))?,
ctbo: ctbo.ok_or_else(|| BmffError::Parse("ctbo box not found, corrupt file?".into()))?,
cmt1: cmt1.ok_or_else(|| BmffError::Parse("cmt1 box not found, corrupt file?".into()))?,
cmt2: cmt2.ok_or_else(|| BmffError::Parse("cmt2 box not found, corrupt file?".into()))?,
cmt3: cmt3.ok_or_else(|| BmffError::Parse("cmt3 box not found, corrupt file?".into()))?,
cmt4: cmt4.ok_or_else(|| BmffError::Parse("cmt4 box not found, corrupt file?".into()))?,
thmb: thmb.ok_or_else(|| BmffError::Parse("thmb box not found, corrupt file?".into()))?,
})
}
}
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Cr3XpacketBox {
pub header: BoxHeader,
}
impl Cr3XpacketBox {
//pub const TYP: FourCC = FourCC::with(['u', 'u', 'i', 'd']);
pub const UUID: [u8; 16] = [0xbe, 0x7a, 0xcf, 0xcb, 0x97, 0xa9, 0x42, 0xe8, 0x9c, 0x71, 0x99, 0x94, 0x91, 0xe3, 0xaf, 0xac];
}
impl<R: Read + Seek> ReadBox<&mut R> for Cr3XpacketBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header })
}
}
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{
super::{BmffError, BoxHeader, FourCC, ReadBox, Result},
cdi1::Cdi1Box,
cmp1::Cmp1Box,
hevc::HevcBox,
jpeg::JpegBox,
};
use crate::formats::bmff::free::FreeBox;
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct CrawBox {
pub header: BoxHeader,
//pub version: u8,
//pub flags: u32,
pub reserved: [u8; 6],
pub data_ref_index: u16,
pub unknown1: [u8; 16],
pub width: u16,
pub height: u16,
pub x_resolution: [u16; 2],
pub y_resolution: [u16; 2],
pub unknown2: u32,
pub unknown3: u16,
pub compr_name: [u8; 32],
pub bit_depth: u16,
pub unknown4: u16,
pub img_flags: u16,
pub img_type: u16,
pub jpeg: Option<JpegBox>,
pub hevc: Option<HevcBox>,
pub cmp1: Option<Cmp1Box>,
pub cdi1: Option<Cdi1Box>,
}
impl CrawBox {
pub const TYP: FourCC = FourCC::with(['C', 'R', 'A', 'W']);
}
impl<R: Read + Seek> ReadBox<&mut R> for CrawBox {
fn read_box(mut reader: &mut R, header: BoxHeader) -> Result<Self> {
//let (version, flags) = read_box_header_ext(reader)?;
let mut reserved = [0_u8; 6];
reader.read_exact(&mut reserved)?;
let data_ref_index = reader.read_u16::<BigEndian>()?;
let mut unknown1 = [0; 16];
reader.read_exact(&mut unknown1)?;
//let _sample_entry = reader.read_u64::<BigEndian>()?;
let width = reader.read_u16::<BigEndian>()?;
let height = reader.read_u16::<BigEndian>()?;
let x_resolution = [reader.read_u16::<BigEndian>()?, reader.read_u16::<BigEndian>()?];
let y_resolution = [reader.read_u16::<BigEndian>()?, reader.read_u16::<BigEndian>()?];
let unknown2 = reader.read_u32::<BigEndian>()?;
let unknown3 = reader.read_u16::<BigEndian>()?;
let mut compr_name = [0_u8; 32];
reader.read_exact(&mut compr_name)?;
let bit_depth = reader.read_u16::<BigEndian>()?;
let unknown4 = reader.read_u16::<BigEndian>()?;
let img_flags = reader.read_u16::<BigEndian>()?;
let img_type = reader.read_u16::<BigEndian>()?;
let mut jpeg = None;
let mut hevc = None;
let mut cmp1 = None;
let mut cdi1 = None;
let mut current = reader.stream_position()?;
while current < header.end_offset() {
// get box?
let header = BoxHeader::parse(&mut reader)?;
match header.typ {
JpegBox::TYP => {
jpeg = Some(JpegBox::read_box(&mut reader, header)?);
}
HevcBox::TYP => {
hevc = Some(HevcBox::read_box(&mut reader, header)?);
}
Cmp1Box::TYP => {
cmp1 = Some(Cmp1Box::read_box(&mut reader, header)?);
}
Cdi1Box::TYP => {
cdi1 = Some(Cdi1Box::read_box(&mut reader, header)?);
}
FreeBox::TYP => {
let _ = Some(FreeBox::read_box(&mut reader, header)?);
}
_ => {
//debug!("Unknown box???");
return Err(BmffError::Parse(format!("Invalid box found in CRAW: {:?}", header.typ)));
}
}
current = reader.stream_position()?;
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
//version,
//flags,
reserved,
data_ref_index,
unknown1,
width,
height,
x_resolution,
y_resolution,
unknown2,
unknown3,
compr_name,
bit_depth,
unknown4,
img_flags,
img_type,
jpeg,
hevc,
cmp1,
cdi1,
})
}
}
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct CtboBox {
pub header: BoxHeader,
}
impl CtboBox {
pub const TYP: FourCC = FourCC::with(['C', 'T', 'B', 'O']);
}
impl<R: Read + Seek> ReadBox<&mut R> for CtboBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
// TODO: add CTBO records
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header })
}
}
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct CtmdBox {
pub header: BoxHeader,
//pub version: u8,
//pub flags: u32,
pub reserved: [u8; 6],
pub data_ref_index: u16,
pub rec_count: u32,
pub records: Vec<CtmdRecord>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct CtmdRecord {
pub unknown1: u8, // 0x00, sometimes 0x01
pub unknown2: u8, // 0x00, seomtimes 0x01
pub rec_type: u16,
pub rec_size: u32,
}
impl CtmdBox {
pub const TYP: FourCC = FourCC::with(['C', 'T', 'M', 'D']);
}
impl<R: Read + Seek> ReadBox<&mut R> for CtmdBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
//let (version, flags) = read_box_header_ext(reader)?;
//let mut reserved = [0_u8; 6];
//reader.read_exact(&mut reserved)?;
//let _reference_index = reader.read_u16::<BigEndian>()?;
let mut reserved = [0_u8; 6];
reader.read_exact(&mut reserved)?;
let data_ref_index = reader.read_u16::<BigEndian>()?;
let rec_count = reader.read_u32::<BigEndian>()?;
let mut records = Vec::with_capacity(rec_count as usize);
//let mut current = reader.seek(SeekFrom::Current(0))?;
for _ in 0..rec_count {
// get box?
let record = CtmdRecord {
unknown1: reader.read_u8()?,
unknown2: reader.read_u8()?,
rec_type: reader.read_u16::<BigEndian>()?,
rec_size: reader.read_u32::<BigEndian>()?,
};
records.push(record);
//current = reader.seek(SeekFrom::Current(0))?;
}
assert!(reader.stream_position()? == header.end_offset());
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
//version,
//flags,
reserved,
data_ref_index,
rec_count,
records,
})
}
}
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HevcBox {
pub header: BoxHeader,
}
impl HevcBox {
pub const TYP: FourCC = FourCC::with(['H', 'E', 'V', 'C']);
}
impl<R: Read + Seek> ReadBox<&mut R> for HevcBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header })
}
}
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Iad1Box {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub img_width: u16,
pub img_height: u16,
pub unknown1: u16,
pub image_type: u16,
pub unknown2: u16,
pub unknown3: u16,
pub iad1_type: Iad1Type,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Iad1Type {
Small(Iad1Small),
Big(Iad1Big),
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Iad1Small {
pub crop_left_offset: u16,
pub crop_top_offset: u16,
pub crop_right_offset: u16,
pub crop_bottom_offset: u16,
pub sensor_x_start: u16,
pub sensor_y_start: u16,
pub sensor_x_end: u16,
pub sensor_y_end: u16,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Iad1Big {
pub crop_left_offset: u16,
pub crop_top_offset: u16,
pub crop_right_offset: u16,
pub crop_bottom_offset: u16,
pub lob_left_offset: u16,
pub lob_top_offset: u16,
pub lob_right_offset: u16,
pub lob_bottom_offset: u16,
pub tob_left_offset: u16,
pub tob_top_offset: u16,
pub tob_right_offset: u16,
pub tob_bottom_offset: u16,
pub active_area_left_offset: u16,
pub active_area_top_offset: u16,
pub active_area_right_offset: u16,
pub active_area_bottom_offset: u16,
}
impl Iad1Box {
pub const TYP: FourCC = FourCC::with(['I', 'A', 'D', '1']);
}
impl<R: Read + Seek> ReadBox<&mut R> for Iad1Box {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
let img_width = reader.read_u16::<BigEndian>()?;
let img_height = reader.read_u16::<BigEndian>()?;
let unknown1 = reader.read_u16::<BigEndian>()?;
let image_type = reader.read_u16::<BigEndian>()?;
let unknown2 = reader.read_u16::<BigEndian>()?;
let unknown3 = reader.read_u16::<BigEndian>()?;
let iad1_type = match image_type {
0 => Iad1Type::Small(Iad1Small {
crop_left_offset: reader.read_u16::<BigEndian>()?,
crop_top_offset: reader.read_u16::<BigEndian>()?,
crop_right_offset: reader.read_u16::<BigEndian>()?,
crop_bottom_offset: reader.read_u16::<BigEndian>()?,
sensor_x_start: reader.read_u16::<BigEndian>()?,
sensor_y_start: reader.read_u16::<BigEndian>()?,
sensor_x_end: reader.read_u16::<BigEndian>()?,
sensor_y_end: reader.read_u16::<BigEndian>()?,
}),
2 => Iad1Type::Big(Iad1Big {
crop_left_offset: reader.read_u16::<BigEndian>()?,
crop_top_offset: reader.read_u16::<BigEndian>()?,
crop_right_offset: reader.read_u16::<BigEndian>()?,
crop_bottom_offset: reader.read_u16::<BigEndian>()?,
lob_left_offset: reader.read_u16::<BigEndian>()?,
lob_top_offset: reader.read_u16::<BigEndian>()?,
lob_right_offset: reader.read_u16::<BigEndian>()?,
lob_bottom_offset: reader.read_u16::<BigEndian>()?,
tob_left_offset: reader.read_u16::<BigEndian>()?,
tob_top_offset: reader.read_u16::<BigEndian>()?,
tob_right_offset: reader.read_u16::<BigEndian>()?,
tob_bottom_offset: reader.read_u16::<BigEndian>()?,
active_area_left_offset: reader.read_u16::<BigEndian>()?,
active_area_top_offset: reader.read_u16::<BigEndian>()?,
active_area_right_offset: reader.read_u16::<BigEndian>()?,
active_area_bottom_offset: reader.read_u16::<BigEndian>()?,
}),
_ => {
panic!("Invalid iad1 type"); // TODO
}
};
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
img_width,
img_height,
unknown1,
image_type,
unknown2,
unknown3,
iad1_type,
})
}
}
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct JpegBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
}
impl JpegBox {
pub const TYP: FourCC = FourCC::with(['J', 'P', 'E', 'G']);
}
impl<R: Read + Seek> ReadBox<&mut R> for JpegBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header, version, flags })
}
}
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
pub mod ccdt;
pub mod cctp;
pub mod cdi1;
pub mod cmp1;
pub mod cmt1;
pub mod cmt2;
pub mod cmt3;
pub mod cmt4;
pub mod cncv;
pub mod cnop;
pub mod cr3desc; // TODO rename to cr3box
pub mod cr3xpacket;
pub mod craw;
pub mod ctbo;
pub mod ctmd;
pub mod hevc;
pub mod iad1;
pub mod jpeg;
pub mod thmb;
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct ThmbBox {
pub header: BoxHeader,
}
impl ThmbBox {
pub const TYP: FourCC = FourCC::with(['T', 'H', 'M', 'B']);
}
impl<R: Read + Seek> ReadBox<&mut R> for ThmbBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
// TODO
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header })
}
}
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result};
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
pub struct FreeBox {
pub header: BoxHeader,
}
impl FreeBox {
pub const TYP: FourCC = FourCC::with(['f', 'r', 'e', 'e']);
}
impl<R: Read + Seek> ReadBox<&mut R> for FreeBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header })
}
}
+46
View File
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BmffError, BoxHeader, FourCC, ReadBox, Result};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct FtypBox {
pub header: BoxHeader,
pub major_brand: FourCC,
pub minor_version: u32,
pub compatible_brands: Vec<FourCC>,
}
impl FtypBox {
pub const TYP: FourCC = FourCC::with(['f', 't', 'y', 'p']);
}
impl<R: Read + Seek> ReadBox<&mut R> for FtypBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let major = reader.read_u32::<BigEndian>()?;
let minor = reader.read_u32::<BigEndian>()?;
if header.size % 4 != 0 {
return Err(BmffError::Parse("invalid ftyp size".into()));
}
let brand_count = (header.size - 16) / 4; // header + major + minor
let mut brands = Vec::new();
for _ in 0..brand_count {
let b = reader.read_u32::<BigEndian>()?;
brands.push(From::from(b));
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
major_brand: From::from(major),
minor_version: minor,
compatible_brands: brands,
})
}
}
+30
View File
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct HdlrBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
}
impl HdlrBox {
pub const TYP: FourCC = FourCC::with(['h', 'd', 'l', 'r']);
}
impl<R: Read + Seek> ReadBox<&mut R> for HdlrBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
// TODO?
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header, version, flags })
}
}
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct MdatBox {
pub header: BoxHeader,
}
impl MdatBox {
pub const TYP: FourCC = FourCC::with(['m', 'd', 'a', 't']);
}
impl<R: Read + Seek> ReadBox<&mut R> for MdatBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header })
}
}
+63
View File
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct MdhdBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub creation_time: u64,
pub modification_time: u64,
pub timescale: u32,
pub duration: u64,
pub language: String,
}
impl MdhdBox {
pub const TYP: FourCC = FourCC::with(['m', 'd', 'h', 'd']);
}
impl<R: Read + Seek> ReadBox<&mut R> for MdhdBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
let (creation_time, modification_time, timescale, duration) = if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
};
let _language_code = reader.read_u16::<BigEndian>()?;
//let language = language_string(language_code); // TODO
let language = String::from("FIXME");
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
creation_time,
modification_time,
timescale,
duration,
language,
})
}
}
+71
View File
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BmffError, BoxHeader, FourCC, ReadBox, Result, hdlr::HdlrBox, mdhd::MdhdBox, minf::MinfBox, vendor::VendorBox};
use log::debug;
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct MdiaBox {
pub header: BoxHeader,
pub mdhd: MdhdBox,
pub hdlr: HdlrBox,
pub minf: MinfBox,
pub vendor: Vec<VendorBox>,
}
impl MdiaBox {
pub const TYP: FourCC = FourCC::with(['m', 'd', 'i', 'a']);
}
impl<R: Read + Seek> ReadBox<&mut R> for MdiaBox {
fn read_box(mut reader: &mut R, header: BoxHeader) -> Result<Self> {
let mut mdhd = None;
let mut hdlr = None;
let mut minf = None;
let mut vendors = Vec::new();
let mut current = reader.stream_position()?;
while current < header.end_offset() {
// get box?
let header = BoxHeader::parse(&mut reader)?;
//let ftyp = Some(FtypBox::read_box(&mut file, header)?);
match header.typ {
MdhdBox::TYP => {
mdhd = Some(MdhdBox::read_box(&mut reader, header)?);
}
HdlrBox::TYP => {
hdlr = Some(HdlrBox::read_box(&mut reader, header)?);
}
MinfBox::TYP => {
minf = Some(MinfBox::read_box(&mut reader, header)?);
}
_ => {
debug!("Vendor box found in mdia: {:?}", header.typ);
let vendor = VendorBox::read_box(&mut reader, header)?;
vendors.push(vendor);
}
}
current = reader.stream_position()?;
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
mdhd: mdhd.ok_or_else(|| BmffError::Parse("mdhd box not found, corrupt file?".into()))?,
hdlr: hdlr.ok_or_else(|| BmffError::Parse("hdlr box not found, corrupt file?".into()))?,
minf: minf.ok_or_else(|| BmffError::Parse("minf box not found, corrupt file?".into()))?,
vendor: vendors,
})
}
}
+72
View File
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BmffError, BoxHeader, FourCC, ReadBox, Result, dinf::DinfBox, stbl::StblBox, vendor::VendorBox, vmhd::VmhdBox};
use log::debug;
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct MinfBox {
pub header: BoxHeader,
pub vmhd: Option<VmhdBox>,
//#[serde(skip_serializing_if = "Option::is_none")]
//pub smhd: Option<SmhdBox>,
pub dinf: DinfBox,
pub stbl: StblBox,
pub vendor: Vec<VendorBox>,
}
impl MinfBox {
pub const TYP: FourCC = FourCC::with(['m', 'i', 'n', 'f']);
}
impl<R: Read + Seek> ReadBox<&mut R> for MinfBox {
fn read_box(mut reader: &mut R, header: BoxHeader) -> Result<Self> {
let mut vmhd = None;
//let mut smhd = None;
let mut dinf = None;
let mut stbl = None;
let mut vendors = Vec::new();
let mut current = reader.stream_position()?;
while current < header.end_offset() {
// get box?
let header = BoxHeader::parse(&mut reader)?;
match header.typ {
VmhdBox::TYP => {
vmhd = Some(VmhdBox::read_box(&mut reader, header)?);
}
DinfBox::TYP => {
dinf = Some(DinfBox::read_box(&mut reader, header)?);
}
StblBox::TYP => {
stbl = Some(StblBox::read_box(&mut reader, header)?);
}
_ => {
debug!("Vendor box found in minf: {:?}", header.typ);
let vendor = VendorBox::read_box(&mut reader, header)?;
vendors.push(vendor);
}
}
current = reader.stream_position()?;
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
vmhd,
dinf: dinf.ok_or_else(|| BmffError::Parse("dinf box not found, corrupt file?".into()))?,
stbl: stbl.ok_or_else(|| BmffError::Parse("stbl box not found, corrupt file?".into()))?,
vendor: vendors,
})
}
}
+327
View File
@@ -0,0 +1,327 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use std::{
fmt,
fs::File,
io::{self, Cursor, Read, Seek, SeekFrom},
};
use byteorder::{BigEndian, ReadBytesExt};
use log::debug;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use thiserror::Error;
pub mod co64;
pub mod dinf;
pub mod ext_cr3;
pub mod free;
pub mod ftyp;
pub mod hdlr;
pub mod mdat;
pub mod mdhd;
pub mod mdia;
pub mod minf;
pub mod moov;
pub mod mvhd;
pub mod skip;
pub mod stbl;
pub mod stsc;
pub mod stsd;
pub mod stsz;
pub mod stts;
pub mod tkhd;
pub mod trak;
pub mod vendor;
pub mod vmhd;
use ftyp::FtypBox;
use mdat::MdatBox;
use moov::MoovBox;
use uuid::Uuid;
use vendor::VendorBox;
use self::ext_cr3::cr3xpacket::Cr3XpacketBox;
type BoxUuid = Uuid;
pub const UUID_TYP: FourCC = FourCC::with(['u', 'u', 'i', 'd']);
pub fn read_box_header_ext<R: Read>(reader: &mut R) -> Result<(u8, u32)> {
let version = reader.read_u8()?;
let flags = reader.read_u24::<BigEndian>()?;
Ok((version, flags))
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct BoxHeader {
pub size: u64,
pub typ: FourCC,
pub uuid: Option<BoxUuid>,
pub offset: u64, // File offset
pub header_len: u64,
// Full Box fields
//pub version: Option<u8>,
//pub flags: Option<u32>,
}
impl BoxHeader {
pub fn parse<R: Read + Seek>(mut reader: R) -> Result<Self> {
let start = reader.stream_position()?;
let mut size = reader.read_u32::<BigEndian>()? as u64;
let typ = reader.read_u32::<BigEndian>()?.into();
if size == 1 {
size = reader.read_u64::<BigEndian>()?;
}
let mut uuid = None;
if typ == UUID_TYP {
let mut buf = [0; 16];
reader.read_exact(&mut buf)?;
uuid = Some(Uuid::from_bytes(buf));
}
let current = reader.stream_position()?;
Ok(BoxHeader {
size,
typ,
uuid,
offset: start,
header_len: current - start,
})
}
pub fn end_offset(&self) -> u64 {
self.offset + self.size
}
pub fn make_view<'a>(&self, buffer: &'a [u8], skip: usize, limit: usize) -> &'a [u8] {
let start = (self.offset + self.header_len) as usize + skip;
if limit == 0 {
&buffer[start..start + (self.size - self.header_len) as usize - skip]
} else {
&buffer[start..start + limit]
}
}
}
pub trait BmffBox: Sized {
fn size(&self) -> u64;
fn data_size(&self) -> u64;
fn box_offset(&self) -> usize;
fn box_data_offset(&self) -> usize;
}
#[derive(Error, Debug)]
pub enum BmffError {
#[error("I/O error while writing DNG")]
Io(#[from] io::Error),
#[error("Parser error: {}", _0)]
Parse(String),
}
type Result<T> = std::result::Result<T, BmffError>;
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct FileBox {
pub ftyp: FtypBox,
pub moov: MoovBox,
pub mdat: MdatBox,
pub vendor: Vec<VendorBox>,
pub cr3xpacket: Option<Cr3XpacketBox>,
}
impl FileBox {
pub fn parse<R: Read + Seek>(mut file: R) -> Result<FileBox> {
let size = file.seek(SeekFrom::End(0))?;
let mut current = file.seek(SeekFrom::Start(0))?;
let mut ftyp = None;
let mut moov = None;
let mut mdat = None;
let mut vendors = Vec::new();
let mut cr3xpacket = None;
while current < size {
// get box?
let header = BoxHeader::parse(&mut file)?;
//let ftyp = Some(FtypBox::read_box(&mut file, header)?);
match header.typ {
FtypBox::TYP => {
debug!("Found ftyp box");
ftyp = Some(FtypBox::read_box(&mut file, header)?);
}
MoovBox::TYP => {
debug!("Found moov box");
moov = Some(MoovBox::read_box(&mut file, header)?);
}
MdatBox::TYP => {
debug!("Found mdat box");
mdat = Some(MdatBox::read_box(&mut file, header)?);
}
_ => {
if let Some(uuid) = header.uuid {
if uuid == Uuid::from_bytes(Cr3XpacketBox::UUID) {
cr3xpacket = Some(Cr3XpacketBox::read_box(&mut file, header)?);
} else {
debug!("Vendor box found in filebox: {:?}", header.typ);
let vendor = VendorBox::read_box(&mut file, header)?;
vendors.push(vendor);
}
} else {
debug!("Vendor box found in filebox: {:?}", header.typ);
let vendor = VendorBox::read_box(&mut file, header)?;
vendors.push(vendor);
}
}
}
if file.stream_position()? != current {
current = file.stream_position()?;
} else {
// If parsing has not moved cursor, we probably have a corrupt file or it's not a BMFF at all.
return Err(BmffError::Parse("Unable to find valid BMFF box, corrupt file?".into()));
}
}
Ok(Self {
ftyp: ftyp.ok_or_else(|| BmffError::Parse("ftyp box not found, corrupt file?".into()))?,
moov: moov.ok_or_else(|| BmffError::Parse("moov box not found, corrupt file?".into()))?,
mdat: mdat.ok_or_else(|| BmffError::Parse("mdat box not found, corrupt file?".into()))?,
vendor: vendors,
cr3xpacket,
})
}
}
pub fn parse_file(file: &mut File) -> Result<FileBox> {
let filebox = FileBox::parse(file)?;
Ok(filebox)
}
pub fn parse_buffer(buf: &[u8]) -> Result<FileBox> {
// TODO: add AsRef<u8>
let mut cursor = Cursor::new(buf);
let filebox = FileBox::parse(&mut cursor)?;
Ok(filebox)
}
#[derive(Default, PartialEq, Eq, Clone, Copy)]
pub struct FourCC {
pub value: [u8; 4],
}
impl Serialize for FourCC {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for FourCC {
fn deserialize<D>(deserializer: D) -> std::result::Result<FourCC, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
let s = String::deserialize(deserializer)?;
if s.len() != 4 {
Err(D::Error::custom(format!("Invalid FourCC value: {}", s)))
} else {
Ok(FourCC {
value: [s.as_bytes()[0], s.as_bytes()[1], s.as_bytes()[2], s.as_bytes()[3]],
})
}
}
}
impl FourCC {
const fn with(v: [char; 4]) -> Self {
Self {
value: [v[0] as u8, v[1] as u8, v[2] as u8, v[3] as u8],
}
}
}
impl From<u32> for FourCC {
fn from(number: u32) -> Self {
FourCC { value: number.to_be_bytes() }
}
}
impl From<FourCC> for u32 {
fn from(fourcc: FourCC) -> u32 {
(&fourcc).into()
}
}
impl From<&FourCC> for u32 {
fn from(fourcc: &FourCC) -> u32 {
u32::from_be_bytes(fourcc.value)
}
}
impl std::str::FromStr for FourCC {
type Err = BmffError;
fn from_str(s: &str) -> Result<Self> {
if let [a, b, c, d] = s.as_bytes() {
Ok(Self { value: [*a, *b, *c, *d] })
} else {
Err(BmffError::Parse("expected exactly four bytes in string".into()))
}
}
}
impl From<[u8; 4]> for FourCC {
fn from(value: [u8; 4]) -> FourCC {
FourCC { value }
}
}
impl fmt::Debug for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let code: u32 = self.into();
let string = String::from_utf8_lossy(&self.value[..]);
write!(f, "{} / {:#010X}", string, code)
}
}
impl fmt::Display for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(&self.value[..]))
}
}
pub trait ReadBox<T>: Sized {
fn read_box(_: T, header: BoxHeader) -> Result<Self>;
}
#[derive(Clone, Debug)]
pub struct Bmff {
pub filebox: FileBox,
}
impl Bmff {
pub fn new<R: Read + Seek>(file: R) -> Result<Self> {
let filebox = FileBox::parse(file)?;
Ok(Self { filebox })
}
pub fn new_buf(buf: &[u8]) -> Result<Self> {
let filebox = parse_buffer(buf)?;
Ok(Self { filebox })
}
pub fn compatible_brand(&self, _brand: &str) -> bool {
true // FIXME
}
}
+82
View File
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BmffError, BoxHeader, FourCC, ReadBox, Result, ext_cr3::cr3desc::Cr3DescBox, mvhd::MvhdBox, trak::TrakBox, vendor::VendorBox};
use log::debug;
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct MoovBox {
pub header: BoxHeader,
pub mvhd: MvhdBox,
//#[serde(skip_serializing_if = "Option::is_none")]
//pub mvex: Option<MvexBox>,
#[serde(rename = "trak")]
pub traks: Vec<TrakBox>,
// UUID Box 85c0b687-820f-11e0-8111-f4ce462b6a48
pub cr3desc: Option<Cr3DescBox>,
pub vendor: Vec<VendorBox>,
}
impl MoovBox {
pub const TYP: FourCC = FourCC::with(['m', 'o', 'o', 'v']);
}
impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
fn read_box(mut reader: &mut R, header: BoxHeader) -> Result<Self> {
let mut mvhd = None;
let mut traks = Vec::new();
let mut cr3desc = None;
let mut vendors = Vec::new();
let mut current = reader.stream_position()?;
while current < header.end_offset() {
// get box?
let header = BoxHeader::parse(&mut reader)?;
match header.typ {
MvhdBox::TYP => {
mvhd = Some(MvhdBox::read_box(&mut reader, header)?);
}
TrakBox::TYP => {
let trak = TrakBox::read_box(&mut reader, header)?;
traks.push(trak);
}
_ => {
if let Some(uuid) = header.uuid {
if uuid == Uuid::from_bytes(Cr3DescBox::UUID) {
cr3desc = Some(Cr3DescBox::read_box(&mut reader, header)?);
}
} else {
debug!("Vendor box found in moov: {:?}", header.typ);
let vendor = VendorBox::read_box(&mut reader, header)?;
vendors.push(vendor);
}
}
}
current = reader.stream_position()?;
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
mvhd: mvhd.ok_or_else(|| BmffError::Parse("mdhd box not found, corrupt file?".into()))?,
traks,
cr3desc,
vendor: vendors,
})
}
}
+62
View File
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct MvhdBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub creation_time: u64,
pub modification_time: u64,
pub timescale: u32,
pub duration: u64,
//#[serde(with = "value_u32")]
//pub rate: FixedPointU16,
}
impl MvhdBox {
pub const TYP: FourCC = FourCC::with(['m', 'v', 'h', 'd']);
}
impl<R: Read + Seek> ReadBox<&mut R> for MvhdBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
let (creation_time, modification_time, timescale, duration) = if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
};
//let rate = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?); // TODO
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
creation_time,
modification_time,
timescale,
duration,
//rate,
})
}
}
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result};
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
pub struct SkipBox {
pub header: BoxHeader,
}
impl SkipBox {
pub const TYP: FourCC = FourCC::with(['s', 'k', 'i', 'p']);
}
impl<R: Read + Seek> ReadBox<&mut R> for SkipBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header })
}
}
+111
View File
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BmffError, BoxHeader, FourCC, ReadBox, Result, co64::Co64Box, stsc::StscBox, stsd::StsdBox, stsz::StszBox, stts::SttsBox, vendor::VendorBox};
use log::debug;
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct StblBox {
pub header: BoxHeader,
pub stsd: StsdBox,
pub stts: SttsBox,
//pub ctts: Option<CttsBox>,
pub stsc: StscBox,
pub stsz: StszBox,
pub co64: Option<Co64Box>,
pub vendor: Vec<VendorBox>,
}
impl StblBox {
pub const TYP: FourCC = FourCC::with(['s', 't', 'b', 'l']);
pub fn get_sample_offset(&self, sample: u32) -> Option<(usize, usize)> {
if let Some(co64) = self.co64.as_ref() {
assert!(sample > 0, "sample number must be greater than 0");
let desc = self.stsc.get_entry_for_sample(sample);
// Chunk number for our sample
let chunk = desc.first_chunk + (((sample - 1) - (desc.first_sample - 1)) / desc.samples_per_chunk);
// Index of sample inside chunk
let chunk_sample_idx = (sample - desc.first_sample) % desc.samples_per_chunk;
// Offset of chunk
let chunk_offset = co64.entries[chunk as usize - 1];
// Offset of sample in chunk
let mut sample_offset = 0;
for i in 0..chunk_sample_idx {
sample_offset += self.stsz.sample_size(sample - chunk_sample_idx + i);
}
let sample_size = self.stsz.sample_size(sample);
Some((chunk_offset as usize + sample_offset as usize, sample_size as usize))
} else {
None
}
}
}
impl<R: Read + Seek> ReadBox<&mut R> for StblBox {
fn read_box(mut reader: &mut R, header: BoxHeader) -> Result<Self> {
let mut stsd = None;
let mut stts = None;
// let mut ctts = None;
//let mut stss = None;
let mut stsc = None;
let mut stsz = None;
//let mut stco = None;
let mut co64 = None;
let mut vendors = Vec::new();
let mut current = reader.stream_position()?;
while current < header.end_offset() {
// get box?
let header = BoxHeader::parse(&mut reader)?;
//let ftyp = Some(FtypBox::read_box(&mut file, header)?);
match header.typ {
StsdBox::TYP => {
stsd = Some(StsdBox::read_box(&mut reader, header)?);
}
SttsBox::TYP => {
stts = Some(SttsBox::read_box(&mut reader, header)?);
}
StscBox::TYP => {
stsc = Some(StscBox::read_box(&mut reader, header)?);
}
StszBox::TYP => {
stsz = Some(StszBox::read_box(&mut reader, header)?);
}
Co64Box::TYP => {
co64 = Some(Co64Box::read_box(&mut reader, header)?);
}
_ => {
debug!("Vendor box found in stbl: {:?}", header.typ);
let vendor = VendorBox::read_box(&mut reader, header)?;
vendors.push(vendor);
}
}
current = reader.stream_position()?;
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
stsd: stsd.ok_or_else(|| BmffError::Parse("stsd box not found, corrupt file?".into()))?,
stts: stts.ok_or_else(|| BmffError::Parse("stts box not found, corrupt file?".into()))?,
stsc: stsc.ok_or_else(|| BmffError::Parse("stsc box not found, corrupt file?".into()))?,
stsz: stsz.ok_or_else(|| BmffError::Parse("stsz box not found, corrupt file?".into()))?,
co64,
vendor: vendors,
})
}
}
+82
View File
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct StscBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub entries: Vec<StscEntry>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct StscEntry {
pub first_chunk: u32,
pub samples_per_chunk: u32,
pub sample_description_index: u32,
pub first_sample: u32,
}
impl StscBox {
pub const TYP: FourCC = FourCC::with(['s', 't', 's', 'c']);
pub fn get_entry_for_sample(&self, sample: u32) -> &StscEntry {
assert!(sample > 0, "Sample numbering starts with 1");
assert_eq!(self.entries.is_empty(), false, "stsc box must contains at least one entry");
assert_eq!(self.entries[0].first_sample, 1, "First entry must start with first sample");
match self.entries.binary_search_by(|entry| entry.first_sample.cmp(&sample)) {
Ok(i) => &self.entries[i],
Err(i) => &self.entries[i - 1],
}
}
}
impl<R: Read + Seek> ReadBox<&mut R> for StscBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
let entry_count = reader.read_u32::<BigEndian>()?;
let mut entries = Vec::with_capacity(entry_count as usize);
// Reader for a single stsc entry
let mut read_entry = || -> Result<StscEntry> {
Ok(StscEntry {
first_chunk: reader.read_u32::<BigEndian>()?,
samples_per_chunk: reader.read_u32::<BigEndian>()?,
sample_description_index: reader.read_u32::<BigEndian>()?,
first_sample: 0,
})
};
if entry_count > 0 {
// Read first entry and hold it back
let mut holdback = read_entry()?;
holdback.first_sample = 1;
for _ in 1..entry_count {
let mut entry = read_entry()?;
// Now we know the chunk count to calc the amount of samples in holdback
entry.first_sample = holdback.first_sample + ((entry.first_chunk - holdback.first_sample) * holdback.samples_per_chunk);
entries.push(holdback);
holdback = entry;
}
// Finalize entry list
entries.push(holdback);
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
entries,
})
}
}
+80
View File
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{
BoxHeader, FourCC, ReadBox, Result,
ext_cr3::{craw::CrawBox, ctmd::CtmdBox},
read_box_header_ext,
vendor::VendorBox,
};
use byteorder::{BigEndian, ReadBytesExt};
use log::debug;
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct StsdBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub craw: Option<CrawBox>,
pub ctmd: Option<CtmdBox>,
pub vendor: Vec<VendorBox>,
}
impl StsdBox {
pub const TYP: FourCC = FourCC::with(['s', 't', 's', 'd']);
}
impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
fn read_box(mut reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
// Canon CR3 boxes
let mut craw = None;
let mut ctmd = None;
let mut vendors = Vec::new();
reader.read_u32::<BigEndian>()?; // FIXME XXX entry_count
let mut current = reader.stream_position()?;
while current < header.end_offset() {
// get box?
let header = BoxHeader::parse(&mut reader)?;
match header.typ {
CrawBox::TYP => {
assert_eq!(craw, None, "Found second CRAW box");
craw = Some(CrawBox::read_box(&mut reader, header)?);
}
CtmdBox::TYP => {
assert_eq!(ctmd, None, "Found second CTMD box");
ctmd = Some(CtmdBox::read_box(&mut reader, header)?);
}
// TODO: Multiple CRAW boxes can occour in CRM files?! BMFF Spec says the SampleBox is an array.
_ => {
debug!("Vendor box found in stsd: {:?}", header.typ);
let vendor = VendorBox::read_box(&mut reader, header)?;
vendors.push(vendor);
}
}
current = reader.stream_position()?;
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
craw,
ctmd,
vendor: vendors,
})
}
}
+60
View File
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct StszBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub sample_size: u32,
pub sample_count: u32,
pub sample_sizes: Vec<u32>,
}
impl StszBox {
pub const TYP: FourCC = FourCC::with(['s', 't', 's', 'z']);
pub fn sample_size(&self, sample: u32) -> u32 {
if self.sample_size > 0 {
self.sample_size
} else {
self.sample_sizes[sample as usize - 1]
}
}
}
impl<R: Read + Seek> ReadBox<&mut R> for StszBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
let sample_size = reader.read_u32::<BigEndian>()?;
let sample_count = reader.read_u32::<BigEndian>()?;
let mut sample_sizes = Vec::with_capacity(sample_count as usize);
if sample_size == 0 {
for _ in 0..sample_count {
let sample_number = reader.read_u32::<BigEndian>()?;
sample_sizes.push(sample_number);
}
} else {
// If the sample_size is non-zero, it is the only sample_size
sample_sizes.push(sample_size);
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
sample_size,
sample_count,
sample_sizes,
})
}
}
+51
View File
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct SttsBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub entries: Vec<SttsEntry>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct SttsEntry {
pub sample_count: u32,
pub sample_delta: u32,
}
impl SttsBox {
pub const TYP: FourCC = FourCC::with(['s', 't', 't', 's']);
}
impl<R: Read + Seek> ReadBox<&mut R> for SttsBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
let entry_count = reader.read_u32::<BigEndian>()?;
let mut entries = Vec::with_capacity(entry_count as usize);
for _i in 0..entry_count {
let entry = SttsEntry {
sample_count: reader.read_u32::<BigEndian>()?,
sample_delta: reader.read_u32::<BigEndian>()?,
};
entries.push(entry);
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
entries,
})
}
}
+30
View File
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct TkhdBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
}
impl TkhdBox {
pub const TYP: FourCC = FourCC::with(['t', 'k', 'h', 'd']);
}
impl<R: Read + Seek> ReadBox<&mut R> for TkhdBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
// TODO?
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self { header, version, flags })
}
}
+68
View File
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BmffError, BoxHeader, FourCC, ReadBox, Result, mdia::MdiaBox, tkhd::TkhdBox, vendor::VendorBox};
use log::debug;
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct TrakBox {
pub header: BoxHeader,
pub tkhd: TkhdBox,
//#[serde(skip_serializing_if = "Option::is_none")]
//pub edts: Option<EdtsBox>,
pub mdia: MdiaBox,
pub vendor: Vec<VendorBox>,
}
impl TrakBox {
pub const TYP: FourCC = FourCC::with(['t', 'r', 'a', 'k']);
}
impl<R: Read + Seek> ReadBox<&mut R> for TrakBox {
fn read_box(mut reader: &mut R, header: BoxHeader) -> Result<Self> {
let mut tkhd = None;
let mut mdia = None;
let mut vendors = Vec::new();
let mut current = reader.stream_position()?;
while current < header.end_offset() {
// get box?
let header = BoxHeader::parse(&mut reader)?;
//let ftyp = Some(FtypBox::read_box(&mut file, header)?);
match header.typ {
TkhdBox::TYP => {
tkhd = Some(TkhdBox::read_box(&mut reader, header)?);
}
MdiaBox::TYP => {
mdia = Some(MdiaBox::read_box(&mut reader, header)?);
}
_ => {
debug!("Vendor box found in trak: {:?}", header.typ);
let vendor = VendorBox::read_box(&mut reader, header)?;
vendors.push(vendor);
}
}
current = reader.stream_position()?;
}
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
tkhd: tkhd.ok_or_else(|| BmffError::Parse("tkhd box not found, corrupt file?".into()))?,
mdia: mdia.ok_or_else(|| BmffError::Parse("mdia box not found, corrupt file?".into()))?,
vendor: vendors,
})
}
}
+19
View File
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, ReadBox, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct VendorBox {
pub header: BoxHeader,
}
impl<R: Read + Seek> ReadBox<&mut R> for VendorBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
reader.seek(SeekFrom::Start(header.offset + header.size))?;
Ok(Self { header })
}
}
+51
View File
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT
// Copyright 2020 Alfred Gutierrez
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{BoxHeader, FourCC, ReadBox, Result, read_box_header_ext};
use byteorder::{BigEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct VmhdBox {
pub header: BoxHeader,
pub version: u8,
pub flags: u32,
pub graphics_mode: u16,
pub op_color: RgbColor,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct RgbColor {
pub red: u16,
pub green: u16,
pub blue: u16,
}
impl VmhdBox {
pub const TYP: FourCC = FourCC::with(['v', 'm', 'h', 'd']);
}
impl<R: Read + Seek> ReadBox<&mut R> for VmhdBox {
fn read_box(reader: &mut R, header: BoxHeader) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader)?;
let graphics_mode = reader.read_u16::<BigEndian>()?;
let op_color = RgbColor {
red: reader.read_u16::<BigEndian>()?,
green: reader.read_u16::<BigEndian>()?,
blue: reader.read_u16::<BigEndian>()?,
};
reader.seek(SeekFrom::Start(header.end_offset()))?;
Ok(Self {
header,
version,
flags,
graphics_mode,
op_color,
})
}
}
+163
View File
@@ -0,0 +1,163 @@
use std::collections::HashMap;
use crate::{bits::*, rawsource::RawSource};
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum CiffTag {
//Null = 0x0000,
ColorInfo1 = 0x0032,
MakeModel = 0x080a,
//ShotInfo = 0x102a,
ColorInfo2 = 0x102c,
WhiteBalance = 0x10a9,
SensorInfo = 0x1031,
//ImageInfo = 0x1810,
DecoderTable = 0x1835,
//RawData = 0x2005,
//SubIFD = 0x300a,
//Exif = 0x300b,
}
fn ct(tag: CiffTag) -> u16 {
tag as u16
}
#[derive(Debug, Clone)]
pub struct CiffEntry {
pub tag: u16,
pub typ: u16,
pub count: usize,
pub bytesize: usize,
pub data_offset: usize,
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct CiffIFD {
entries: HashMap<u16, CiffEntry>,
subifds: Vec<CiffIFD>,
}
pub fn is_ciff(file: &RawSource) -> bool {
match file.subview(0, 14) {
Ok(buf) => buf[6..14] == b"HEAPCCDR"[..],
Err(_) => false,
}
}
impl CiffIFD {
pub fn new_file(file: &RawSource) -> Result<CiffIFD, String> {
let data = file.as_vec().map_err(|err| format!("Failed to read whole file: {:?}", err))?;
CiffIFD::new(&data, LEu32(&data, 2) as usize, data.len(), 1)
}
pub fn new(buf: &[u8], start: usize, end: usize, depth: u32) -> Result<CiffIFD, String> {
let mut entries = HashMap::new();
let mut subifds = Vec::new();
let valuedata_size = LEu32(buf, end - 4) as usize;
let dircount = LEu16(buf, start + valuedata_size) as usize;
for i in 0..dircount {
let entry_offset: usize = start + valuedata_size + 2 + i * 10;
let e = CiffEntry::new(buf, start, entry_offset)?;
if e.typ == 0x2800 || e.typ == 0x3000 {
// SubIFDs
if depth < 10 {
// Avoid infinite looping IFDs
let ifd = CiffIFD::new(buf, e.data_offset, e.data_offset + e.bytesize, depth + 1);
match ifd {
Ok(val) => {
subifds.push(val);
}
Err(_) => {
entries.insert(e.tag, e);
} // Ignore unparsable IFDs
}
}
} else {
entries.insert(e.tag, e);
}
}
Ok(CiffIFD { entries, subifds })
}
pub fn find_entry(&self, tag: CiffTag) -> Option<&CiffEntry> {
if self.entries.contains_key(&ct(tag)) {
self.entries.get(&ct(tag))
} else {
for ifd in &self.subifds {
if let Some(x) = ifd.find_entry(tag) {
return Some(x);
}
}
None
}
}
}
impl CiffEntry {
pub fn new(buf: &[u8], value_data: usize, offset: usize) -> Result<CiffEntry, String> {
let p = LEu16(buf, offset);
let tag = p & 0x3fff;
let datalocation = (p & 0xc000) as usize;
let typ = p & 0x3800;
let (bytesize, data_offset) = match datalocation {
// Data is offset in value_data
0x0000 => (LEu32(buf, offset + 2) as usize, LEu32(buf, offset + 6) as usize + value_data),
// Data is stored directly in entry
0x4000 => (8, offset + 2),
val => return Err(format!("CIFF: Don't know about data location {:x}", val)),
};
let data = &buf[data_offset..data_offset + bytesize];
let count = bytesize >> CiffEntry::element_shift(typ);
Ok(CiffEntry {
tag,
typ,
count,
bytesize,
data_offset,
data: Vec::from(data),
})
}
pub fn element_shift(typ: u16) -> usize {
match typ {
// Byte and ASCII
0x0000 | 0x8000 => 0,
// Short
0x1000 => 1,
// Long, Mix, Sub1 and Sub2
0x1800 | 0x2000 | 0x2800 | 0x3000 => 2,
// Default to 0
_ => 0,
}
}
pub fn get_strings(&self) -> Vec<String> {
String::from_utf8_lossy(&self.data).split_terminator('\0').map(|x| x.to_string()).collect()
}
pub fn get_u32(&self, idx: usize) -> u32 {
match self.typ {
0x0000 | 0x8000 => self.data[idx] as u32,
0x1000 => LEu16(&self.data, idx * 2) as u32,
0x1800 | 0x2000 | 0x2800 | 0x3000 => LEu32(&self.data, idx * 4),
_ => panic!("{}", format!("Trying to read typ {} for a u32", self.typ)),
}
}
pub fn get_usize(&self, idx: usize) -> usize {
self.get_u32(idx) as usize
}
pub fn get_f32(&self, idx: usize) -> f32 {
self.get_u32(idx) as f32
}
pub fn get_force_u16(&self, idx: usize) -> u16 {
LEu16(&self.data, idx * 2)
}
}
+322
View File
@@ -0,0 +1,322 @@
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{Read, Seek, SeekFrom};
use thiserror::Error;
use crate::formats::tiff::IFD;
use crate::rawsource::RawSource;
pub type Result<T> = std::result::Result<T, JfifError>;
/// Error variants for JFIF parser
#[derive(Debug, Error)]
pub enum JfifError {
/// Overflow of input, size constraints...
#[error("Overflow error: {}", _0)]
Overflow(String),
#[error("General error: {}", _0)]
General(String),
#[error("Format mismatch: {}", _0)]
FormatMismatch(String),
/// Error on internal cursor type
#[error("I/O error: {:?}", _0)]
Io(#[from] std::io::Error),
}
pub trait ReadSegment<T>: Sized {
fn read_segment(_: T, symbol: u16) -> Result<Self>;
}
#[derive(Debug, Clone, Default)]
pub struct App0 {
pub len: u64,
pub version: u16,
pub density_units: u8,
pub xdensity: u16,
pub ydensity: u16,
pub xthumbnail: u8,
pub ythumbnail: u8,
pub thumbnail: Option<Vec<u8>>,
}
impl<R: Read + Seek> ReadSegment<&mut R> for App0 {
fn read_segment(reader: &mut R, _symbol: u16) -> Result<Self> {
//let pos = reader.stream_position()?;
let len: u64 = reader.read_u16::<BigEndian>()? as u64;
const APP1_JFIF_MARKER: [u8; 5] = [b'J', b'F', b'I', b'F', b'\0'];
if len as usize >= APP1_JFIF_MARKER.len() {
let mut jfif_str = [0; 5];
reader.read_exact(&mut jfif_str)?;
if jfif_str == APP1_JFIF_MARKER {
let version = reader.read_u16::<BigEndian>()?;
let density_units = reader.read_u8()?;
let xdensity = reader.read_u16::<BigEndian>()?;
let ydensity = reader.read_u16::<BigEndian>()?;
let xthumbnail = reader.read_u8()?;
let ythumbnail = reader.read_u8()?;
let thumbnail = if xthumbnail * ythumbnail > 0 {
let mut data = vec![0; (3 * xthumbnail * ythumbnail) as usize];
reader.read_exact(&mut data)?;
Some(data)
} else {
None
};
Ok(Self {
len,
version,
density_units,
xdensity,
ydensity,
xthumbnail,
ythumbnail,
thumbnail,
})
} else {
Err(JfifError::FormatMismatch("Failed to find JFIF marker in APP0 segment".into()))
}
} else {
Err(JfifError::FormatMismatch("APP0 segment is too short".into()))
}
}
}
#[derive(Debug, Clone)]
pub struct App1 {
pub len: u64,
pub payload: Payload,
}
#[derive(Debug, Clone)]
pub struct SOS {
pub len: u64,
}
impl<R: Read + Seek> ReadSegment<&mut R> for SOS {
fn read_segment(reader: &mut R, _symbol: u16) -> Result<Self> {
let pos = reader.stream_position()?;
let mut prev = 0;
loop {
let v = reader.read_u8()?;
if prev == 0xFF && v == 0xFF {
prev = 0;
continue;
}
if prev == 0xFF && (v & !0x7) == 0xD0 {
prev = 0;
continue;
}
if prev == 0xFF && v == 0 {
prev = 0;
continue;
}
if prev == 0xFF {
reader.seek(SeekFrom::Current(-2))?;
break;
}
prev = v;
}
let len = reader.stream_position()? - pos;
Ok(Self { len })
}
}
#[derive(Debug, Clone)]
pub enum Payload {
Exif(IFD),
Xpacket(Vec<u8>),
Unknown,
}
impl<R: Read + Seek> ReadSegment<&mut R> for App1 {
fn read_segment(reader: &mut R, _symbol: u16) -> Result<Self> {
let pos = reader.stream_position()?;
let len: u64 = reader.read_u16::<BigEndian>()? as u64;
const APP1_EXIF_MARKER: [u8; 6] = [b'E', b'x', b'i', b'f', b'\0', b'\0'];
if len as usize >= APP1_EXIF_MARKER.len() {
let mut exif_str = [0; 6];
reader.read_exact(&mut exif_str)?;
if exif_str == APP1_EXIF_MARKER {
let ifd = IFD::new_root(reader, pos as u32 + 2 + 6);
reader.seek(SeekFrom::Start(pos + len))?;
/*
for i in ifd.dump::<TiffCommonTag>(10) {
println!("{}", i);
}
for i in ifd.get_sub_ifds(TiffCommonTag::ExifIFDPointer).unwrap()[0].dump::<ExifTag>(10) {
println!("{}", i);
}
*/
if let Ok(ifd) = ifd {
return Ok(Self {
len,
payload: Payload::Exif(ifd),
});
}
return Err(JfifError::General("Failed to read exif".into()));
} else {
reader.seek(SeekFrom::Current(-(exif_str.len() as i64)))?;
}
}
const APP1_XMP_MARKER: &str = concat!("http://ns.adobe.com/xap/1.0/", '\0');
if len as usize >= APP1_XMP_MARKER.len() {
let mut xmp_str = [0; APP1_XMP_MARKER.len()];
reader.read_exact(&mut xmp_str)?;
if xmp_str == APP1_XMP_MARKER.as_bytes() {
log::debug!("Found APP1 XMP marker");
let mut xpacket = vec![0; len as usize - xmp_str.len() - 2];
reader.read_exact(&mut xpacket)?;
reader.seek(SeekFrom::Start(pos + len))?;
return Ok(Self {
len,
payload: Payload::Xpacket(xpacket),
});
} else {
reader.seek(SeekFrom::Current(-(xmp_str.len() as i64)))?;
}
}
reader.seek(SeekFrom::Start(pos + len))?;
Ok(Self {
len,
payload: Payload::Unknown,
})
}
}
#[derive(Debug, Clone)]
pub enum Segment {
SOI { offset: u64 },
APP0 { offset: u64, app0: App0 },
APP1 { offset: u64, app1: App1 },
SOS { offset: u64, sos: SOS },
EOI,
Unknown { offset: u64, marker: u16 },
}
#[derive(Debug, Clone)]
pub struct Jfif {
pub segments: Vec<Segment>,
}
impl Jfif {
pub fn parse<R: Read + Seek>(mut reader: R) -> Result<Self> {
let mut segments = Vec::new();
let mut pos = reader.stream_position()?;
let mut sym = Some(reader.read_u16::<BigEndian>()?);
while let Some(symbol) = sym {
log::debug!("Found symbol: {:X}", symbol);
let segment = match symbol {
0xFFD8 => Segment::SOI { offset: pos },
0xFFD9 => Segment::EOI,
0xFFE0 => Segment::APP0 {
offset: pos,
app0: App0::read_segment(&mut reader, symbol)?,
},
0xFFE1 => match App1::read_segment(&mut reader, symbol) {
Ok(app1) => Segment::APP1 { offset: pos, app1 },
Err(err) => {
log::warn!("Failed to read APP1 (EXIF) segement, maybe corrupt: {:?}", err);
continue;
}
},
0xFFDA => Segment::SOS {
offset: pos,
sos: SOS::read_segment(&mut reader, symbol)?,
},
_ => {
log::debug!("Unhandled JFIF segment marker: {:X}", symbol);
let len: u64 = reader.read_u16::<BigEndian>()? as u64;
reader.seek(SeekFrom::Current(len as i64 - 2))?;
Segment::Unknown { offset: pos, marker: symbol }
}
};
if segments.is_empty() && sym != Some(0xFFD8) {
return Err(JfifError::General("first marker must be SOI".into()));
}
segments.push(segment);
if symbol == 0xFFD9 {
break; // EOI reached
}
pos = reader.stream_position()?;
sym = reader.read_u16::<BigEndian>().ok();
}
if !segments.is_empty() {
Ok(Self { segments })
} else {
Err(JfifError::FormatMismatch("JFIF contains no segements".into()))
}
}
pub fn new(file: &RawSource) -> Result<Self> {
Self::parse(&mut file.reader())
}
pub fn exif_ifd(&self) -> Option<&IFD> {
self.segments.iter().find_map(|seg| match seg {
Segment::APP1 {
app1: App1 {
payload: Payload::Exif(ifd), ..
},
..
} => Some(ifd),
_ => None,
})
}
pub fn xpacket(&self) -> Option<&Vec<u8>> {
self.segments.iter().find_map(|seg| match seg {
Segment::APP1 {
app1: App1 {
payload: Payload::Xpacket(xpacket),
..
},
..
} => Some(xpacket),
_ => None,
})
}
}
pub fn is_jfif(file: &RawSource) -> bool {
match file.subview(0, 4) {
Ok(buf) => {
let result = buf[0..4] == [0xFF, 0xD8, 0xFF, 0xE0];
if !result {
//panic!("Failed: {:x} {:x} {:x} {:x}", buf[0], buf[1], buf[2], buf[3]);
}
result
}
Err(err) => {
log::error!("is_jfif(): {:?}", err);
false
}
}
}
pub fn is_exif(file: &RawSource) -> bool {
match file.subview(0, 4) {
Ok(buf) => buf[0..4] == [0xFF, 0xD8, 0xFF, 0xE1],
Err(err) => {
log::error!("is_exif(): {:?}", err);
false
}
}
}
+7
View File
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: LGPL-2.1
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
pub mod bmff;
pub mod ciff;
pub mod jfif;
pub mod tiff;
+245
View File
@@ -0,0 +1,245 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use std::io::{Read, Seek};
use log::debug;
use serde::{Deserialize, Serialize};
use crate::{
bits::Endian,
formats::tiff::{Rational, SRational, TiffAscii, TiffError, Value, apply_corr, reader::ReadByteOrder},
};
use super::{Result, reader::EndianReader};
const TYPE_BYTE: u16 = 1;
const TYPE_ASCII: u16 = 2;
const TYPE_SHORT: u16 = 3;
const TYPE_LONG: u16 = 4;
const TYPE_RATIONAL: u16 = 5;
const TYPE_SBYTE: u16 = 6;
const TYPE_UNDEFINED: u16 = 7;
const TYPE_SSHORT: u16 = 8;
const TYPE_SLONG: u16 = 9;
const TYPE_SRATIONAL: u16 = 10;
const TYPE_FLOAT: u16 = 11;
const TYPE_DOUBLE: u16 = 12;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Entry {
pub tag: u16,
pub value: Value,
// Embedded value for writer, offset for reader
// This is only None when building an IFD for writing.
pub embedded: Option<u32>, // TODO: rename it
}
impl std::ops::Deref for Entry {
type Target = Value;
fn deref(&self) -> &Self::Target {
&self.value
}
}
pub struct RawEntry<'a> {
pub entry: &'a Entry,
pub endian: Endian,
pub data: Vec<u8>,
}
impl<'a> RawEntry<'a> {
pub fn get_force_u32(&self, idx: usize) -> u32 {
self.endian.read_u32(&self.data, idx * 4)
}
pub fn get_force_u16(&self, idx: usize) -> u16 {
self.endian.read_u16(&self.data, idx * 2)
}
}
// 0-1-2-3-4-5-6-7-8-9-10-11-12-13
const DATASHIFTS: [u8; 14] = [0, 0, 0, 1, 2, 3, 0, 0, 1, 2, 3, 2, 3, 2];
impl Entry {
pub fn value_type(&self) -> u16 {
self.value.value_type()
}
pub fn count(&self) -> u32 {
self.value.count() as u32
}
/// Returns the offset
/// It is already corrected by `corr` but needs to be summed
/// with `base` offset.
pub fn offset(&self) -> Option<usize> {
self.embedded.map(|v| v as usize)
}
pub fn parse<R: Read + Seek>(reader: &mut EndianReader<R>, base: u32, corr: i32, tag: u16) -> Result<Entry> {
let pos = reader.position()? - 2; // TODO -2 because tag is already read
let typ = reader.read_u16()?;
let count = reader.read_u32()?;
// If we don't know the type assume byte data (undefined)
let compat_typ = if typ == 0 || typ > 12 { 7 } else { typ };
let bytesize: usize = (count as usize) << DATASHIFTS[compat_typ as usize];
let offset: u32 = if bytesize <= 4 {
reader.position()? - base
} else {
apply_corr(reader.read_u32()?, corr)
};
debug!(
"Tag: {:#x}, Typ: {:#x}, count: {}, offset: {}, base: {}, corr: {}",
tag, typ, count, offset, base, corr
);
if offset == u32::MAX || base.checked_add(offset).is_none() {
// We hit an invalid offset, ignoring this tag
// This happens for Olympus E-P2 images in ImageProc IFD for example.
return Err(TiffError::Overflow(format!("Offset {} is invalid for tag 0x{:X}", offset, tag)));
}
reader.goto(base + offset)?;
let entry = match typ {
TYPE_BYTE => {
let mut v = vec![0; count as usize];
reader.read_u8_into(&mut v)?;
Entry {
tag,
value: Value::Byte(v),
embedded: Some(offset),
}
}
TYPE_ASCII => {
let mut v = vec![0; count as usize];
reader.read_u8_into(&mut v)?;
Entry {
tag,
value: Value::Ascii(TiffAscii::new_from_raw(&v)),
embedded: Some(offset),
}
}
TYPE_SHORT => {
let mut v = vec![0; count as usize];
reader.read_u16_into(&mut v)?;
Entry {
tag,
value: Value::Short(v),
embedded: Some(offset),
}
}
TYPE_LONG => {
let mut v = vec![0; count as usize];
reader.read_u32_into(&mut v)?;
Entry {
tag,
value: Value::Long(v),
embedded: Some(offset),
}
}
TYPE_RATIONAL => {
let mut tmp = vec![0; count as usize * 2]; // Rational is 2x u32
reader.read_u32_into(&mut tmp)?;
let mut v = Vec::with_capacity(count as usize);
for chunk in tmp.chunks_exact(2) {
v.push(Rational::new(chunk[0], chunk[1]));
}
Entry {
tag,
value: Value::Rational(v),
embedded: Some(offset),
}
}
TYPE_SBYTE => {
let mut v = vec![0; count as usize];
reader.read_i8_into(&mut v)?;
Entry {
tag,
value: Value::SByte(v),
embedded: Some(offset),
}
}
TYPE_UNDEFINED => {
let mut v = vec![0; count as usize];
reader.read_u8_into(&mut v)?;
Entry {
tag,
value: Value::Undefined(v),
embedded: Some(offset),
}
}
TYPE_SSHORT => {
let mut v = vec![0; count as usize];
reader.read_i16_into(&mut v)?;
Entry {
tag,
value: Value::SShort(v),
embedded: Some(offset),
}
}
TYPE_SLONG => {
let mut v = vec![0; count as usize];
reader.read_i32_into(&mut v)?;
Entry {
tag,
value: Value::SLong(v),
embedded: Some(offset),
}
}
TYPE_SRATIONAL => {
let mut tmp = vec![0; count as usize * 2]; // SRational is 2x i32
reader.read_i32_into(&mut tmp)?;
let mut v = Vec::with_capacity(count as usize);
for chunk in tmp.chunks_exact(2) {
v.push(SRational::new(chunk[0], chunk[1]));
}
Entry {
tag,
value: Value::SRational(v),
embedded: Some(offset),
}
}
TYPE_FLOAT => {
let mut v = vec![0.0; count as usize];
reader.read_f32_into(&mut v)?;
Entry {
tag,
value: Value::Float(v),
embedded: Some(offset),
}
}
TYPE_DOUBLE => {
let mut v = vec![0.0; count as usize];
reader.read_f64_into(&mut v)?;
Entry {
tag,
value: Value::Double(v),
embedded: Some(offset),
}
}
x => {
let mut v = vec![0; count as usize];
reader.read_u8_into(&mut v)?;
Entry {
tag,
value: Value::Unknown(x, v),
embedded: Some(offset),
}
}
};
reader.goto(pos + 12)?; // Size of IFD entry
Ok(entry)
}
pub fn type_name(&self) -> String {
self.value.value_type_name()
}
}
+26
View File
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::IFD;
use serde::{Deserialize, Serialize};
/// Reader for TIFF files
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct TiffFile {
/// Chain of all IFDs in TIFF
pub chain: Vec<IFD>,
/// Base offset, starting from file or buffer (good for embedded TIFF in other structures)
pub base: u32,
/// Offset correction value
pub corr: i32,
}
impl TiffFile {
pub fn new(base: u32, corr: i32) -> Self {
Self { base, corr, chain: Vec::new() }
}
pub fn push_ifd(&mut self, ifd: IFD) {
self.chain.push(ifd);
}
}
+679
View File
@@ -0,0 +1,679 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{
Entry, Result, TiffError, Value, apply_corr,
entry::RawEntry,
read_from_file,
reader::{EndianReader, ReadByteOrder},
};
use crate::{
bits::Endian,
rawsource::RawSource,
tags::{ExifTag, TiffCommonTag, TiffTag},
};
use byteorder::{LittleEndian, ReadBytesExt};
use log::debug;
use serde::{Deserialize, Serialize};
use std::{
collections::{BTreeMap, HashMap},
io::{Read, Seek, SeekFrom},
};
const MAX_IFD_ENTRIES: usize = 4096;
#[derive(Debug)]
pub enum OffsetMode {
Absolute,
RelativeToIFD,
}
#[derive(Debug)]
pub enum DataMode {
Strips,
Tiles,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct IFD {
pub offset: u32,
pub base: u32,
pub corr: i32,
pub next_ifd: u32,
pub entries: BTreeMap<u16, Entry>,
pub endian: Endian,
pub sub: HashMap<u16, Vec<IFD>>,
pub chain: Vec<IFD>,
}
// TODO: fixme
impl IFD {
/// Construct new IFD from reader at specific base
pub fn new_root<R: Read + Seek>(reader: &mut R, base: u32) -> Result<IFD> {
Self::new_root_with_correction(reader, 0, base, 0, 10, &[TiffCommonTag::SubIFDs.into(), TiffCommonTag::ExifIFDPointer.into()])
}
pub fn new_root_with_correction<R: Read + Seek>(reader: &mut R, offset: u32, base: u32, corr: i32, max_chain: usize, sub_tags: &[u16]) -> Result<IFD> {
reader.seek(SeekFrom::Start((base + offset) as u64))?;
let endian = match reader.read_u16::<LittleEndian>()? {
0x4949 => Endian::Little,
0x4d4d => Endian::Big,
x => {
return Err(TiffError::General(format!("TIFF: don't know marker 0x{:x}", x)));
}
};
let mut reader = EndianReader::new(reader, endian);
let magic = reader.read_u16()?;
if magic != 42 {
return Err(TiffError::General(format!("Invalid magic marker for TIFF: {}", magic)));
}
let mut next_ifd = reader.read_u32()?;
if next_ifd == 0 {
return Err(TiffError::General("Invalid TIFF header, contains no root IFD".to_string()));
}
let reader = reader.into_inner();
let mut multi_sub_tags = vec![];
multi_sub_tags.extend_from_slice(sub_tags);
next_ifd = apply_corr(next_ifd, corr);
let mut root = IFD::new(reader, next_ifd, base, corr, endian, &multi_sub_tags)?;
if root.entries.is_empty() {
return Err(TiffError::General("TIFF is invalid, IFD must contain at least one entry".to_string()));
}
next_ifd = root.next_ifd;
while next_ifd != 0 {
next_ifd = apply_corr(next_ifd, corr);
let ifd = IFD::new(reader, next_ifd, base, corr, endian, &multi_sub_tags)?;
if ifd.entries.is_empty() {
return Err(TiffError::General("TIFF is invalid, IFD must contain at least one entry".to_string()));
}
next_ifd = ifd.next_ifd;
root.chain.push(ifd);
if root.chain.len() > max_chain && max_chain > 0 {
break;
}
}
Ok(root)
}
pub fn new<R: Read + Seek>(reader: &mut R, offset: u32, base: u32, corr: i32, endian: Endian, sub_tags: &[u16]) -> Result<IFD> {
reader.seek(SeekFrom::Start((base + offset) as u64))?;
let mut sub_ifd_offsets = HashMap::new();
let mut reader = EndianReader::new(reader, endian);
let entry_count = reader.read_u16()?;
if entry_count as usize > MAX_IFD_ENTRIES {
log::warn!(
"TIFF: IFD entry count {} is suspicious (limit {}). The file might be corrupt.",
entry_count,
MAX_IFD_ENTRIES
);
}
let mut entries = BTreeMap::new();
let mut sub = HashMap::new();
let mut next_pos = reader.position()?;
debug!("Parse entries");
let mut consecutive_errors = 0;
for i in 0..entry_count {
if i as usize >= MAX_IFD_ENTRIES {
log::warn!(
"TIFF: Reached maximum IFD entry limit ({}). Stopping parse to prevent infinite loops.",
MAX_IFD_ENTRIES
);
break;
}
if let Err(e) = reader.goto(next_pos) {
log::warn!("Truncated IFD: Could not seek to next entry position. Stopping parse. Error: {}", e);
break;
}
next_pos += 12;
let tag = match reader.read_u16() {
Ok(t) => t,
Err(e) => {
log::warn!("Truncated IFD: Could not read tag ID (Index {}). Stopping parse. Error: {}", i, e);
break;
}
};
match Entry::parse(&mut reader, base, corr, tag) {
Ok(entry) => {
consecutive_errors = 0;
if sub_tags.contains(&tag) {
match &entry.value {
Value::Long(offsets) => {
sub_ifd_offsets.insert(tag, offsets.clone());
}
Value::Unknown(tag, offsets) => {
sub_ifd_offsets.insert(*tag, vec![offsets[0] as u32]);
}
Value::Undefined(_) => {
sub_ifd_offsets.insert(tag, vec![entry.offset().unwrap() as u32]);
}
val => {
log::info!(
"Found IFD offset tag, but type mismatch: {:?}. Ignoring SubIFD parsing for tag 0x{:X}",
val,
tag
);
}
}
}
entries.insert(entry.tag, entry);
}
Err(err) => {
consecutive_errors += 1;
log::warn!("Failed to parse TIFF tag 0x{:X} (Index {}). Error: {:?}", tag, i, err);
// If we fail 5 times in a row, the IFD is likely garbage or physically truncated.
if consecutive_errors >= 5 {
log::warn!("Too many consecutive parsing errors ({}). Stopping parse to prevent flood.", consecutive_errors);
break;
}
}
}
}
// Some TIFF writers skip the next ifd pointer
// If we get an I/O error, we fallback to 0, signaling the end of IFD chains.
let next_ifd = match reader.read_u32() {
Ok(ptr) => ptr,
Err(e) => {
debug!(
"TIFF IFD reader failed to get next IFD pointer, fallback to 0 and continue. Original error was: {}",
e
);
0
}
};
// Process SubIFDs
let pos = reader.position()?;
let reader = reader.into_inner();
for subs in sub_ifd_offsets {
let mut ifds = Vec::new();
for offset in subs.1 {
match Self::new(reader, apply_corr(offset, corr), base, corr, endian, &[]) {
Ok(ifd) => ifds.push(ifd),
Err(err) => {
log::warn!("Error while processing TIFF sub-IFD for tag 0x{:X}, ignoring it: {}", subs.0, err);
}
};
}
sub.insert(subs.0, ifds);
}
EndianReader::new(reader, endian).goto(pos)?; // restore
Ok(IFD {
offset,
base,
corr,
next_ifd: if next_ifd == 0 { 0 } else { apply_corr(next_ifd, corr) },
entries,
endian,
sub,
chain: vec![],
})
}
pub fn copy_tag(dst: &mut Self, src: &Self, tag: impl Into<u16>) {
if let Some(entry) = src.get_entry(tag.into()) {
dst.entries.insert(entry.tag, entry.clone());
}
}
pub fn value_iter(&self) -> impl Iterator<Item = (&u16, &Value)> {
self.entries().iter().map(|(tag, entry)| (tag, &entry.value))
}
/*
pub fn new<R: Read + Seek>(reader: &mut R, offset: u32, base: u32, corr: i32, endian: Endian, sub_tags: &[u16]) -> Result<Self> {
reader.seek(SeekFrom::Start((base + offset) as u64))?;
let mut sub_ifd_offsets = Vec::new();
let mut reader = EndianReader::new(reader, endian);
let entry_count = reader.read_u16()?;
let mut entries = BTreeMap::new();
let mut sub = Vec::new();
for _ in 0..entry_count {
//let embedded = reader.read_u32()?;
let tag = reader.read_u16()?;
if tag == LegacyTiffRootTag::SubIFDs.into() || sub_tags.contains(&tag) {
let entry = Entry::parse(&mut reader, base, corr, tag)?;
match entry.value {
Value::Long(offsets) => {
sub_ifd_offsets.extend_from_slice(&offsets);
}
_ => {
todo!()
}
}
} else {
let entry = Entry::parse(&mut reader, base, corr, tag)?;
entries.insert(entry.tag, entry);
}
}
let next_ifd = reader.read_u32()?;
// Process SubIFDs
let pos = reader.position()?;
let reader = reader.into_inner();
for offset in sub_ifd_offsets {
let ifd = IFD::new(reader, apply_corr(offset, corr), base, corr, endian, sub_tags)?;
sub.push(ifd);
}
EndianReader::new(reader, endian).goto(pos)?; // restore
Ok(Self {
offset,
base,
corr,
next_ifd: if next_ifd == 0 { 0 } else { apply_corr(next_ifd, corr) },
entries,
endian,
sub,
})
}
*/
/// Extend the IFD with sub-IFDs from a specific tag.
/// The IFD corrections are used from current IFD.
pub fn extend_sub_ifds<R: Read + Seek>(&mut self, reader: &mut R, tag: u16) -> Result<Option<&Vec<Self>>> {
if let Some(entry) = self.get_entry(tag) {
let mut subs = Vec::new();
match &entry.value {
Value::Long(offsets) => {
for off in offsets {
let ifd = Self::new_root_with_correction(reader, *off, self.base, self.corr, 10, &[])?;
subs.push(ifd);
}
self.sub.insert(tag, subs);
Ok(self.sub.get(&tag))
}
val => {
debug!("Found IFD offset tag, but type mismatch: {:?}", val);
todo!()
}
}
} else {
Ok(None)
}
}
pub fn extend_sub_ifds_custom<R, F>(&mut self, reader: &mut R, tag: u16, op: F) -> Result<Option<&Vec<Self>>>
where
R: Read + Seek,
F: FnOnce(&mut R, &IFD, &Entry) -> Result<Option<Vec<IFD>>>,
{
if let Some(entry) = self.get_entry(tag) {
if let Some(subs) = op(reader, self, entry)? {
self.sub.insert(tag, subs);
Ok(self.sub.get(&tag))
} else {
Ok(None)
}
} else {
Ok(None)
}
}
pub fn sub_ifds(&self) -> &HashMap<u16, Vec<IFD>> {
&self.sub
}
pub fn entry_count(&self) -> u16 {
self.entries.len() as u16
}
pub fn next_ifd(&self) -> u32 {
self.next_ifd
}
pub fn entries(&self) -> &BTreeMap<u16, Entry> {
&self.entries
}
pub fn get_entry<T: TiffTag>(&self, tag: T) -> Option<&Entry> {
self.entries.get(&tag.into())
}
pub fn get_entry_subs<T: TiffTag>(&self, tag: T) -> Option<&Entry> {
for subs in &self.sub {
for ifd in subs.1 {
if let Some(entry) = ifd.get_entry_recursive(tag) {
return Some(entry);
}
}
}
None
}
pub fn get_entry_recursive<T: TiffTag>(&self, tag: T) -> Option<&Entry> {
self.entries.get(&tag.into()).or_else(|| self.get_entry_subs(tag))
}
pub fn get_entry_raw<'a, T: TiffTag, R: Read + Seek>(&'a self, tag: T, file: &mut R) -> Result<Option<RawEntry<'a>>> {
if let Some(entry) = self.get_entry(tag) {
return Ok(Some(RawEntry {
entry,
endian: self.endian,
data: read_from_file(file, self.base + entry.offset().unwrap() as u32, entry.byte_size())?,
}));
}
Ok(None)
}
/// Get the data of a tag by just reading as many `len` bytes from offet.
pub fn get_entry_raw_with_len<'a, T: TiffTag, R: Read + Seek>(&'a self, tag: T, file: &mut R, len: usize) -> Result<Option<RawEntry<'a>>> {
if let Some(entry) = self.get_entry(tag) {
return Ok(Some(RawEntry {
entry,
endian: self.endian,
data: read_from_file(file, self.base + entry.offset().unwrap() as u32, len)?,
}));
}
Ok(None)
}
pub fn get_sub_ifd_all<T: TiffTag>(&self, tag: T) -> Option<&Vec<IFD>> {
self.sub.get(&tag.into())
}
pub fn get_sub_ifd<T: TiffTag>(&self, tag: T) -> Option<&IFD> {
if let Some(ifds) = self.get_sub_ifd_all(tag) {
if ifds.len() == 1 {
ifds.get(0)
} else {
log::warn!(
"get_sub_ifd() for tag {:?} found more IFDs than expected: {}. Fallback to first IFD!",
tag,
ifds.len()
);
ifds.get(0)
}
} else {
None
}
}
pub fn get_new_sub_file_type(&self) -> Option<u16> {
if let Some(entry) = self.get_entry(TiffCommonTag::NewSubFileType) {
Some(entry.force_u16(0))
} else {
None
}
}
pub fn find_ifds_with_tag<T: TiffTag>(&self, tag: T) -> Vec<&IFD> {
let mut ifds = Vec::new();
if self.get_entry(tag).is_some() {
ifds.push(self);
}
// Now search in all sub IFDs
for subs in self.sub_ifds() {
for ifd in subs.1 {
ifds.append(&mut ifd.find_ifds_with_tag(tag));
}
}
ifds
}
pub fn find_first_ifd_with_tag<T: TiffTag>(&self, tag: T) -> Option<&IFD> {
self.find_ifds_with_tag(tag).get(0).copied()
}
/*
pub fn get_ifd<T: TiffTagEnum, R: Read + Seek>(&self, tag: T, reader: &mut R) -> Result<Option<IFD>> {
if let Some(offset) = self.get_entry(tag) {
match &offset.value {
Value::Long(v) => {
debug!("IFD offset: {}", v[0]);
Ok(Some(IFD::new(reader, apply_corr(v[0], self.corr), self.base, self.corr, self.endian, &[])?))
}
_ => {
return Err(TiffError::General(format!(
"TIFF tag {:?} is not of type LONG, thus can not be used as IFD offset in get_ifd().",
tag
)));
}
}
} else {
Ok(None)
}
}
*/
pub fn has_entry<T: TiffTag>(&self, tag: T) -> bool {
self.get_entry(tag).is_some()
}
pub fn sub_buf<R: Read + Seek>(&self, reader: &mut R, offset: usize, len: usize) -> Result<Vec<u8>> {
//&buf[self.start_offset+offset..self.start_offset+offset+len]
let mut buf = vec![0; len];
reader.seek(SeekFrom::Start(self.base as u64 + offset as u64))?;
reader.read_exact(&mut buf)?;
Ok(buf)
}
pub fn contains_singlestrip_image(&self) -> bool {
self.get_entry(TiffCommonTag::StripOffsets).map(Entry::count).unwrap_or(0) == 1
}
pub fn singlestrip_data_rawsource<'a>(&self, rawsource: &'a RawSource) -> Result<&'a [u8]> {
assert!(self.contains_singlestrip_image());
let offset = self
.get_entry(TiffCommonTag::StripOffsets)
.ok_or_else(|| TiffError::General(("tag not found").to_string()))?
.value
.force_u32(0);
let len = self
.get_entry(TiffCommonTag::StripByteCounts)
.ok_or_else(|| TiffError::General(("tag not found").to_string()))?
.value
.force_usize(0);
Ok(rawsource.subview((self.base + offset) as u64, len as u64)?)
}
/// Return byte slices to strip data.
/// If there exists a single strip only or if all strips are continous,
/// the second return value contains the whole strip data in a single slice.
pub fn strip_data<'a>(&self, rawsource: &'a RawSource) -> Result<(Vec<&'a [u8]>, Option<&'a [u8]>)> {
if !self.has_entry(TiffCommonTag::StripOffsets) {
return Err(TiffError::General("IFD contains no strip data".into()));
}
let offsets = if let Some(Entry { value: Value::Long(data), .. }) = self.get_entry(TiffCommonTag::StripOffsets) {
data
} else {
return Err(TiffError::General("Invalid datatype for StripOffsets".to_string()));
};
let sizes = if let Some(Entry { value: Value::Long(data), .. }) = self.get_entry(TiffCommonTag::StripByteCounts) {
data
} else {
return Err(TiffError::General("Invalid datatype for StripByteCounts".to_string()));
};
if offsets.len() != sizes.len() {
return Err(TiffError::General(format!(
"Can't get data from strips: offsets has len {} but sizes has len {}",
offsets.len(),
sizes.len()
)));
}
// Check if all slices are continous
let (is_continous, end_off) =
offsets.iter().zip(sizes.iter()).fold(
(true, offsets[0]),
|acc, val| {
if acc.0 && acc.1 == *val.0 { (true, acc.1 + *val.1) } else { (false, 0) }
},
);
let mut subviews = Vec::with_capacity(offsets.len());
for (offset, size) in offsets.iter().zip(sizes.iter()) {
subviews.push(rawsource.subview((self.base + *offset) as u64, *size as u64)?);
}
let continous = if is_continous {
Some(rawsource.subview((self.base + offsets[0]) as u64, (end_off - offsets[0]) as u64)?)
} else {
None
};
Ok((subviews, continous))
}
pub fn tile_data<'a>(&self, rawsource: &'a RawSource) -> Result<Vec<&'a [u8]>> {
let offsets = if let Some(Entry { value: Value::Long(data), .. }) = self.get_entry(TiffCommonTag::TileOffsets) {
data
} else {
return Err(TiffError::General("Invalid datatype for TileOffsets".to_string()));
};
let byte_counts = if let Some(Entry { value: Value::Long(data), .. }) = self.get_entry(TiffCommonTag::TileByteCounts) {
data
} else {
return Err(TiffError::General("Invalid datatype for TileByteCounts".to_string()));
};
let mut tile_slices = Vec::with_capacity(offsets.len());
offsets.iter().zip(byte_counts.iter()).for_each(|(offset, size)| {
tile_slices.push(rawsource.subview(*offset as u64, *size as u64).map_err(TiffError::Io));
});
Ok(tile_slices.into_iter().collect::<Result<Vec<_>>>()?)
}
/// Check for the data mode (Strips or Tiles)
pub fn data_mode(&self) -> Result<DataMode> {
if self.has_entry(TiffCommonTag::StripOffsets) {
Ok(DataMode::Strips)
} else if self.has_entry(TiffCommonTag::TileOffsets) {
Ok(DataMode::Tiles)
} else {
Err(TiffError::General("IFD has no StripOffsets or TileOffsets tag".into()))
}
}
pub fn parse_makernote<R: Read + Seek>(&self, reader: &mut R, offset_mode: OffsetMode, sub_tags: &[u16]) -> Result<Option<IFD>> {
if let Some(exif) = self.get_entry(ExifTag::MakerNotes) {
let offset = exif.offset().unwrap() as u32;
debug!("Makernote offset: {}", offset);
match &exif.value {
Value::Undefined(data) => {
let mut off = 0;
let mut endian = self.endian;
// Olympus starts the makernote with their own name, sometimes truncated
if data[0..5] == b"OLYMP"[..] {
off += 8;
if data[0..7] == b"OLYMPUS"[..] {
off += 4;
}
}
// Epson starts the makernote with its own name
if data[0..5] == b"EPSON"[..] {
off += 8;
}
// Fujifilm has 12 extra bytes
if data[0..8] == b"FUJIFILM"[..] {
off += 12;
}
// Sony has 12 extra bytes
if data[0..9] == b"SONY DSC "[..] {
off += 12;
}
// Pentax makernote starts with AOC\0 - If it's there, skip it
if data[0..4] == b"AOC\0"[..] {
off += 4;
}
// Pentax can also start with PENTAX and in that case uses different offsets
if data[0..6] == b"PENTAX"[..] {
off += 8;
let endian = if data[off..off + 2] == b"II"[..] { Endian::Little } else { Endian::Big };
// All offsets in this IFD are relative to the start of this tag,
// so wie use the offset as correction value.
let corr = offset as i32;
// The IFD itself starts 10 bytes after tag offset.
return Ok(Some(IFD::new(reader, offset + 10, self.base, corr, endian, sub_tags)?));
}
if data[0..7] == b"Nikon\0\x02"[..] {
off += 10;
let endian = if data[off..off + 2] == b"II"[..] { Endian::Little } else { Endian::Big };
return Ok(Some(IFD::new(reader, 8, self.base + offset + 10, 0, endian, sub_tags)?));
}
// Some have MM or II to indicate endianness - read that
if data[off..off + 2] == b"II"[..] {
off += 2;
endian = Endian::Little;
}
if data[off..off + 2] == b"MM"[..] {
off += 2;
endian = Endian::Big;
}
match offset_mode {
OffsetMode::Absolute => Ok(Some(IFD::new(reader, offset + off as u32, self.base, self.corr, endian, sub_tags)?)),
OffsetMode::RelativeToIFD => {
// Value offsets are relative to IFD offset
let corr = offset + off as u32;
Ok(Some(IFD::new(reader, offset + off as u32, self.base, corr as i32, endian, sub_tags)?))
}
}
}
_ => Err(TiffError::General("EXIF makernote has unknown type".to_string())),
}
} else {
Ok(None)
}
}
pub fn dump<T: TiffTag>(&self, limit: usize) -> Vec<String> {
let mut out = Vec::new();
out.push(format!("IFD entries: {}\n", self.entries.len()));
out.push(format!("{0:<34} | {1:<10} | {2:<6} | {3}\n", "Tag", "Type", "Count", "Data"));
for (tag, entry) in &self.entries {
let mut line = String::new();
let tag_name = {
if let Ok(name) = T::try_from(*tag) {
format!("{:?}", name)
} else {
format!("<?{}>", tag)
}
};
line.push_str(&format!(
"{0:#06x} : {0:<6} {1:<20}| {2:<10} | {3:<6} | ",
tag,
tag_name,
entry.type_name(),
entry.count()
));
line.push_str(&entry.visual_rep(limit));
out.push(line);
}
for subs in self.sub_ifds().iter() {
for (i, sub) in subs.1.iter().enumerate() {
out.push(format!("SubIFD({}:{})", subs.0, i));
for line in sub.dump::<T>(limit) {
out.push(format!(" {}", line));
}
}
}
out
}
}
+375
View File
@@ -0,0 +1,375 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use std::io::{Read, Seek, SeekFrom};
use thiserror::Error;
pub mod entry;
pub mod file;
pub mod ifd;
pub mod reader;
pub mod value;
pub mod writer;
pub use entry::Entry;
pub use ifd::IFD;
pub use reader::GenericTiffReader;
pub use value::{IntoTiffValue, Rational, SRational, TiffAscii, Value};
pub use writer::{DirectoryWriter, TiffWriter};
const TIFF_MAGIC: u16 = 42;
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum CompressionMethod {
None = 1,
Huffman = 2,
Fax3 = 3,
Fax4 = 4,
LZW = 5,
OldJPEG = 6, // TODO: RENAME
// "Extended JPEG" or "new JPEG" style
ModernJPEG = 7,
Deflate = 8,
OldDeflate = 0x80B2,
PackBits = 0x8005,
// DNG Extensions
LossyJPEG = 34892,
JPEGXL = 52546,
}
impl ExtractFromIFD for CompressionMethod {
fn extract(ifd: &IFD) -> Result<Option<Self>> {
let tag = TiffCommonTag::Compression;
if let Some(entry) = ifd.get_entry(tag) {
let x = entry.value.force_u16(0);
Ok(Some(
Self::n(x).ok_or_else(|| TiffError::General(format!("Unknown value {} for {:?}", x, tag)))?,
))
} else {
Ok(None)
}
}
}
impl From<CompressionMethod> for Value {
fn from(value: CompressionMethod) -> Self {
Value::Short(vec![value as u16])
}
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum PhotometricInterpretation {
WhiteIsZero = 0,
BlackIsZero = 1,
RGB = 2,
RGBPalette = 3,
TransparencyMask = 4,
CMYK = 5,
YCbCr = 6,
CIELab = 8,
// Defined by DNG
CFA = 32803,
LinearRaw = 34892,
}
impl ExtractFromIFD for PhotometricInterpretation {
fn extract(ifd: &IFD) -> Result<Option<Self>> {
let tag = TiffCommonTag::PhotometricInt;
if let Some(entry) = ifd.get_entry(tag) {
let x = entry.value.force_u16(0);
Ok(Some(
Self::n(x).ok_or_else(|| TiffError::General(format!("Unknown value {} for {:?}", x, tag)))?,
))
} else {
Ok(None)
}
}
}
impl From<PhotometricInterpretation> for Value {
fn from(value: PhotometricInterpretation) -> Self {
Value::Short(vec![value as u16])
}
}
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum PreviewColorSpace {
Unknown = 0,
GrayGamma = 1,
SRgb = 2,
AdobeRGB = 3,
ProPhotoRGB = 5,
}
impl ExtractFromIFD for PreviewColorSpace {
fn extract(ifd: &IFD) -> Result<Option<Self>> {
let tag = DngTag::PreviewColorSpace;
if let Some(entry) = ifd.get_entry(tag) {
let x = entry.value.force_u16(0);
Ok(Some(
Self::n(x).ok_or_else(|| TiffError::General(format!("Unknown value {} for {:?}", x, tag)))?,
))
} else {
Ok(None)
}
}
}
impl From<PreviewColorSpace> for Value {
fn from(value: PreviewColorSpace) -> Self {
Value::Long(vec![value as u32])
}
}
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum PlanarConfiguration {
Chunky = 1,
Planar = 2,
}
impl ExtractFromIFD for PlanarConfiguration {
fn extract(ifd: &IFD) -> Result<Option<Self>> {
let tag = ExifTag::PlanarConfiguration;
if let Some(entry) = ifd.get_entry(tag) {
let x = entry.value.force_u16(0);
Ok(Some(
Self::n(x).ok_or_else(|| TiffError::General(format!("Unknown value {} for {:?}", x, tag)))?,
))
} else {
Ok(None)
}
}
}
impl From<PlanarConfiguration> for Value {
fn from(value: PlanarConfiguration) -> Self {
Value::Short(vec![value as u16])
}
}
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum Predictor {
None = 1,
Horizontal = 2,
}
impl ExtractFromIFD for Predictor {
fn extract(ifd: &IFD) -> Result<Option<Self>> {
let tag = TiffCommonTag::Predictor;
if let Some(entry) = ifd.get_entry(tag) {
let x = entry.value.force_u16(0);
Ok(Some(
Self::n(x).ok_or_else(|| TiffError::General(format!("Unknown value {} for {:?}", x, tag)))?,
))
} else {
Ok(None)
}
}
}
impl From<Predictor> for Value {
fn from(value: Predictor) -> Self {
Value::Short(vec![value as u16])
}
}
/// Type to represent resolution units
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum ResolutionUnit {
None = 1,
Inch = 2,
Centimeter = 3,
}
impl ExtractFromIFD for ResolutionUnit {
fn extract(ifd: &IFD) -> Result<Option<Self>> {
let tag = TiffCommonTag::Predictor;
if let Some(entry) = ifd.get_entry(tag) {
let x = entry.value.force_u16(0);
Ok(Some(
Self::n(x).ok_or_else(|| TiffError::General(format!("Unknown value {} for {:?}", x, tag)))?,
))
} else {
Ok(None)
}
}
}
impl From<ResolutionUnit> for Value {
fn from(value: ResolutionUnit) -> Self {
Value::Short(vec![value as u16])
}
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Copy, Clone, PartialEq, enumn::N)]
#[repr(u16)]
pub enum SampleFormat {
Uint = 1,
Int = 2,
IEEEFP = 3,
Void = 4,
}
impl From<SampleFormat> for Value {
fn from(value: SampleFormat) -> Self {
Value::Short(vec![value as u16])
}
}
impl ExtractFromIFD for SampleFormat {
fn extract(ifd: &IFD) -> Result<Option<Self>> {
let tag = TiffCommonTag::SampleFormat;
if let Some(entry) = ifd.get_entry(tag) {
let x = entry.value.force_u16(0);
Ok(Some(
Self::n(x).ok_or_else(|| TiffError::General(format!("Unknown value {} for {:?}", x, tag)))?,
))
} else {
Ok(None)
}
}
}
pub trait ExtractFromIFD
where
Self: Sized,
{
fn extract(ifd: &IFD) -> Result<Option<Self>>;
}
/// Error variants for compressor
#[derive(Debug, Error)]
pub enum TiffError {
/// Overflow of input, size constraints...
#[error("Overflow error: {}", _0)]
Overflow(String),
#[error("General error: {}", _0)]
General(String),
#[error("Format mismatch: {}", _0)]
FormatMismatch(String),
/// Error on internal cursor type
#[error("I/O error: {:?}", _0)]
Io(#[from] std::io::Error),
}
/// Result type for Compressor results
pub type Result<T> = std::result::Result<T, TiffError>;
use crate::tags::{DngTag, ExifTag, TiffCommonTag};
/*
impl From<Value> for Entry {
fn from(value: Value) -> Self {
Entry { value, embedded: None }
}
}
*/
pub struct DataOffset {
pub offset: usize,
}
fn apply_corr(offset: u32, corr: i32) -> u32 {
((offset as i64) + (corr as i64)) as u32
}
pub struct DirReader {}
fn read_from_file<R: Read + Seek>(file: &mut R, offset: u32, size: usize) -> Result<Vec<u8>> {
file.seek(SeekFrom::Start(offset as u64))?;
let mut buf = vec![0; size];
file.read_exact(&mut buf)?;
Ok(buf)
}
#[cfg(test)]
mod tests {
use std::io::{Cursor, Seek, SeekFrom};
use crate::{formats::tiff::reader::TiffReader, tags::TiffCommonTag};
use super::*;
#[test]
fn encode_tiff_test() -> std::result::Result<(), Box<dyn std::error::Error>> {
let mut buf = Cursor::new(Vec::new());
let mut tiff = TiffWriter::new(&mut buf)?;
let mut dir = tiff.new_directory();
let offset = {
let mut dir2 = tiff.new_directory();
dir2.add_tag(32_u16, 23_u16);
dir2.build(&mut tiff)?
};
dir.add_tag(TiffCommonTag::ActiveArea, offset as u16);
dir.add_tag(TiffCommonTag::ActiveArea, [23_u16, 45_u16]);
dir.add_tag(TiffCommonTag::ActiveArea, &[23_u16, 45_u16][..]);
dir.add_tag(TiffCommonTag::ActiveArea, "Fobbar");
Ok(())
}
#[test]
fn write_tiff_file_basic() -> std::result::Result<(), Box<dyn std::error::Error>> {
let mut output = Cursor::new(Vec::new());
let mut tiff = TiffWriter::new(&mut output)?;
let mut dir = tiff.new_directory();
let offset = {
let mut dir2 = tiff.new_directory();
dir2.add_tag(32_u16, 23_u16);
dir2.build(&mut tiff)?
};
dir.add_tag(TiffCommonTag::ExifIFDPointer, offset);
dir.add_tag(TiffCommonTag::ActiveArea, [9_u16, 10_u16, 11_u16, 12, 13, 14]);
dir.add_tag(TiffCommonTag::BlackLevels, [9_u16, 10_u16]);
dir.add_tag(TiffCommonTag::WhiteLevel, [11_u16]);
dir.add_tag(TiffCommonTag::BitsPerSample, [12_u32]);
dir.add_tag(TiffCommonTag::ResolutionUnit, [-5_i32]);
dir.add_tag(TiffCommonTag::Artist, "AT");
tiff.build(dir)?;
//assert!(TiffReader::is_tiff(&mut output) == true);
let mut garbage_output: Vec<u8> = vec![0x4a, 0xee]; // Garbage
garbage_output.extend_from_slice(&output.into_inner());
let mut garbage_output = Cursor::new(garbage_output);
garbage_output.seek(SeekFrom::Start(2))?;
let reader = GenericTiffReader::new(&mut garbage_output, 1, 1, Some(16), &[])?; // 1 byte offset correction
assert_eq!(reader.root_ifd().entry_count(), 7);
assert!(reader.root_ifd().get_entry(TiffCommonTag::WhiteLevel).is_some());
assert!(matches!(
reader.root_ifd().get_entry(TiffCommonTag::ExifIFDPointer).unwrap().value,
Value::Long { .. }
));
assert!(matches!(
reader.root_ifd().get_entry(TiffCommonTag::WhiteLevel).unwrap().value,
Value::Short { .. }
));
Ok(())
}
}
+429
View File
@@ -0,0 +1,429 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use super::{Entry, IFD, Result, TiffError, apply_corr, entry::RawEntry, file::TiffFile};
use crate::{
bits::Endian,
tags::{ExifTag, TiffCommonTag, TiffTag},
};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
use serde::{Deserialize, Serialize};
use std::io::{Cursor, Read, Seek, SeekFrom};
pub trait TiffReader {
fn file(&self) -> &TiffFile;
fn file_mut(&mut self) -> &mut TiffFile;
fn chains(&self) -> &Vec<IFD> {
&self.file().chain
}
fn get_endian(&self) -> Endian {
self.root_ifd().endian
}
/// Returns a list of well-known tags representing SubIFDs.
fn wellknown_sub_ifd_tags(&self) -> Vec<u16> {
vec![
TiffCommonTag::SubIFDs.into(),
TiffCommonTag::ExifIFDPointer.into(),
ExifTag::GPSInfo.into(),
ExifTag::IccProfile.into(),
]
}
fn root_ifd(&self) -> &IFD {
if self.file().chain.is_empty() {
panic!("TIFF must have at least one root IFD but the IFD list is empty");
}
&self.file().chain[0]
}
fn get_entry<T: TiffTag>(&self, tag: T) -> Option<&Entry> {
for ifd in &self.file().chain {
if let Some(x) = ifd.get_entry(tag) {
return Some(x);
}
}
None
}
fn get_entry_raw<'a, T: TiffTag, R: Read + Seek>(&'a self, tag: T, file: &mut R) -> Result<Option<RawEntry<'a>>> {
for ifd in &self.file().chain {
if let Some(entry) = ifd.get_entry_raw(tag, file)? {
return Ok(Some(entry));
}
}
Ok(None)
}
fn has_entry<T: TiffTag>(&self, tag: T) -> bool {
self.get_entry(tag).is_some()
}
fn find_ifds_with_tag<T: TiffTag>(&self, tag: T) -> Vec<&IFD> {
let mut ifds = Vec::new();
for ifd in &self.file().chain {
if ifd.has_entry(tag) {
ifds.push(ifd);
}
// Now search in all sub IFDs
for subs in ifd.sub_ifds() {
for ifd in subs.1 {
if ifd.has_entry(tag) {
ifds.push(ifd);
}
}
}
}
ifds
}
fn find_ifds_with_filter<F: Fn(&IFD) -> bool>(&self, filter: F) -> Vec<&IFD> {
let mut ifds = Vec::new();
for ifd in &self.file().chain {
if filter(ifd) {
ifds.push(ifd);
}
// Now search in all sub IFDs
for subs in ifd.sub_ifds() {
for ifd in subs.1 {
if filter(ifd) {
ifds.push(ifd);
}
}
}
}
ifds
}
fn find_ifd_with_new_subfile_type(&self, typ: u32) -> Option<&IFD> {
let list = self.find_ifds_with_tag(TiffCommonTag::NewSubFileType);
list
.iter()
.find(|ifd| ifd.get_entry(TiffCommonTag::NewSubFileType).expect("IFD must contain this entry").force_u32(0) == typ)
.copied()
}
// TODO: legacy wrapper
fn find_first_ifd<T: TiffTag>(&self, tag: T) -> Option<&IFD> {
self.find_first_ifd_with_tag(tag)
}
fn find_first_ifd_with_tag<T: TiffTag>(&self, tag: T) -> Option<&IFD> {
let ifds = self.find_ifds_with_tag(tag);
if ifds.is_empty() { None } else { Some(ifds[0]) }
}
fn get_first_entry(&self, _tag: u16) -> Option<Entry> {
unimplemented!();
/*
Some(Entry {
value: (32 as u16).into(),
embedded: None,
})
*/
}
fn read_data<R: Read + Seek>(&self, file: &mut R, uncorr_offset: u32, buffer: &mut [u8]) -> Result<()> {
file.seek(SeekFrom::Start(apply_corr(uncorr_offset, self.file().corr) as u64))?;
file.read_exact(buffer)?;
Ok(())
}
fn parse_ifd<R: Read + Seek>(&self, reader: &mut R, offset: u32, base: u32, corr: i32, endian: Endian, sub_tags: &[u16]) -> Result<IFD> {
IFD::new(reader, offset, base, corr, endian, sub_tags)
}
/// Construct a TIFF reader from Read capable objects
///
/// `corr` is a correction value that should be applied to offsets received
/// from file structure.
fn parse_file<R: Read + Seek>(&mut self, file: &mut R, max_chained: Option<usize>, sub_tags: &[u16]) -> Result<()> {
let endian = match file.read_u16::<LittleEndian>()? {
0x4949 => Endian::Little,
0x4d4d => Endian::Big,
x => {
return Err(TiffError::General(format!("TIFF: don't know marker 0x{:x}", x)));
}
};
let mut reader = EndianReader::new(file, endian);
let magic = reader.read_u16()?;
if magic != 42 {
//return Err(TiffError::General(format!("Invalid magic marker for TIFF: {}", magic)));
}
let mut next_ifd = reader.read_u32()?;
if next_ifd == 0 {
return Err(TiffError::General("Invalid TIFF header, contains no root IFD".to_string()));
}
let reader = reader.into_inner();
next_ifd = apply_corr(next_ifd, self.file().corr);
let mut chain = Vec::new();
while next_ifd != 0 {
// TODO: check if offset is in range
let mut multi_sub_tags = self.wellknown_sub_ifd_tags();
multi_sub_tags.extend_from_slice(sub_tags);
let ifd = IFD::new(reader, next_ifd, self.file().base, self.file().corr, endian, &multi_sub_tags)?;
if ifd.entries.is_empty() {
return Err(TiffError::General("TIFF is invalid, IFD must contain at least one entry".to_string()));
}
next_ifd = ifd.next_ifd;
chain.push(ifd);
if let Some(max) = max_chained {
if chain.len() > max {
break;
}
}
}
if chain.is_empty() {
return Err(TiffError::General("TIFF is invalid, must contain at least one IFD".to_string()));
}
self.file_mut().chain = chain;
Ok(())
}
}
/// Reader for TIFF files
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct GenericTiffReader {
file: TiffFile,
}
impl TiffReader for GenericTiffReader {
fn file(&self) -> &TiffFile {
&self.file
}
fn file_mut(&mut self) -> &mut TiffFile {
&mut self.file
}
}
impl GenericTiffReader {
/// Check if buffer looks like a TIFF file
pub fn is_tiff<T: AsRef<[u8]>>(buffer: T) -> bool {
let buffer = buffer.as_ref();
buffer[0] == 0x49 || buffer[0] == 0x4d // TODO
}
pub fn little_endian(&self) -> bool {
self.file.chain.first().unwrap().endian == Endian::Little
}
/// Construct a TIFF reader from a byte buffer
///
/// Byte buffer must be a full TIFF file structure, endianess is detected from TIFF
/// header.
///
/// `corr` is a correction value that should be applied to offsets received
/// from file structure.
pub fn new_with_buffer<T: AsRef<[u8]>>(buffer: T, base: u32, corr: i32, max_chained: Option<usize>) -> Result<Self> {
let mut cursor = Cursor::new(buffer.as_ref());
cursor.seek(SeekFrom::Start(base as u64))?;
Self::new(&mut cursor, base, corr, max_chained, &[])
}
/// Construct a TIFF reader from Read capable objects
///
/// `corr` is a correction value that should be applied to offsets received
/// from file structure.
pub fn new<R: Read + Seek>(file: &mut R, base: u32, corr: i32, max_chained: Option<usize>, sub_tags: &[u16]) -> Result<Self> {
let mut ins = Self {
file: TiffFile::new(base, corr),
};
ins.parse_file(file, max_chained, sub_tags)?;
Ok(ins)
}
}
pub trait ReadByteOrder {
fn read_u8(&mut self) -> std::io::Result<u8>;
fn read_i8(&mut self) -> std::io::Result<i8>;
fn read_u16(&mut self) -> std::io::Result<u16>;
fn read_i16(&mut self) -> std::io::Result<i16>;
fn read_u32(&mut self) -> std::io::Result<u32>;
fn read_i32(&mut self) -> std::io::Result<i32>;
fn read_u64(&mut self) -> std::io::Result<u64>;
fn read_i64(&mut self) -> std::io::Result<i64>;
fn read_f32(&mut self) -> std::io::Result<f32>;
fn read_f64(&mut self) -> std::io::Result<f64>;
fn read_u8_into(&mut self, dst: &mut [u8]) -> std::io::Result<()>;
fn read_i8_into(&mut self, dst: &mut [i8]) -> std::io::Result<()>;
fn read_u16_into(&mut self, dst: &mut [u16]) -> std::io::Result<()>;
fn read_i16_into(&mut self, dst: &mut [i16]) -> std::io::Result<()>;
fn read_u32_into(&mut self, dst: &mut [u32]) -> std::io::Result<()>;
fn read_i32_into(&mut self, dst: &mut [i32]) -> std::io::Result<()>;
fn read_u64_into(&mut self, dst: &mut [u64]) -> std::io::Result<()>;
fn read_i64_into(&mut self, dst: &mut [i64]) -> std::io::Result<()>;
fn read_f32_into(&mut self, dst: &mut [f32]) -> std::io::Result<()>;
fn read_f64_into(&mut self, dst: &mut [f64]) -> std::io::Result<()>;
}
pub struct EndianReader<'a, R: Read + Seek + 'a> {
endian: Endian,
inner: &'a mut R,
}
impl<'a, R: Read + Seek + 'a> EndianReader<'a, R> {
pub fn new(inner: &'a mut R, endian: Endian) -> Self {
Self { endian, inner }
}
pub fn into_inner(self) -> &'a mut R {
self.inner
}
pub fn inner(&'a mut self) -> &'a mut R {
self.inner
}
pub fn position(&mut self) -> Result<u32> {
Ok(self.inner.stream_position().map(|v| v as u32)?)
}
// TODO: try_from?
pub fn goto(&mut self, offset: u32) -> Result<()> {
self.inner.seek(SeekFrom::Start(offset as u64))?;
Ok(())
// TODO: try_from?
}
}
impl<'a, R: Read + Seek + 'a> ReadByteOrder for EndianReader<'a, R> {
fn read_u16(&mut self) -> std::io::Result<u16> {
match self.endian {
Endian::Little => self.inner.read_u16::<LittleEndian>(),
Endian::Big => self.inner.read_u16::<BigEndian>(),
}
}
fn read_u8(&mut self) -> std::io::Result<u8> {
self.inner.read_u8()
}
fn read_i8(&mut self) -> std::io::Result<i8> {
match self.endian {
Endian::Little => self.inner.read_i8(),
Endian::Big => self.inner.read_i8(),
}
}
fn read_i16(&mut self) -> std::io::Result<i16> {
match self.endian {
Endian::Little => self.inner.read_i16::<LittleEndian>(),
Endian::Big => self.inner.read_i16::<BigEndian>(),
}
}
fn read_u32(&mut self) -> std::io::Result<u32> {
match self.endian {
Endian::Little => self.inner.read_u32::<LittleEndian>(),
Endian::Big => self.inner.read_u32::<BigEndian>(),
}
}
fn read_i32(&mut self) -> std::io::Result<i32> {
match self.endian {
Endian::Little => self.inner.read_i32::<LittleEndian>(),
Endian::Big => self.inner.read_i32::<BigEndian>(),
}
}
fn read_u64(&mut self) -> std::io::Result<u64> {
match self.endian {
Endian::Little => self.inner.read_u64::<LittleEndian>(),
Endian::Big => self.inner.read_u64::<BigEndian>(),
}
}
fn read_i64(&mut self) -> std::io::Result<i64> {
match self.endian {
Endian::Little => self.inner.read_i64::<LittleEndian>(),
Endian::Big => self.inner.read_i64::<BigEndian>(),
}
}
fn read_f32(&mut self) -> std::io::Result<f32> {
match self.endian {
Endian::Little => self.inner.read_f32::<LittleEndian>(),
Endian::Big => self.inner.read_f32::<BigEndian>(),
}
}
fn read_f64(&mut self) -> std::io::Result<f64> {
match self.endian {
Endian::Little => self.inner.read_f64::<LittleEndian>(),
Endian::Big => self.inner.read_f64::<BigEndian>(),
}
}
fn read_u8_into(&mut self, dst: &mut [u8]) -> std::io::Result<()> {
self.inner.read_exact(dst)
}
fn read_i8_into(&mut self, dst: &mut [i8]) -> std::io::Result<()> {
self.inner.read_i8_into(dst)
}
fn read_u16_into(&mut self, dst: &mut [u16]) -> std::io::Result<()> {
match self.endian {
Endian::Little => self.inner.read_u16_into::<LittleEndian>(dst),
Endian::Big => self.inner.read_u16_into::<BigEndian>(dst),
}
}
fn read_i16_into(&mut self, dst: &mut [i16]) -> std::io::Result<()> {
match self.endian {
Endian::Little => self.inner.read_i16_into::<LittleEndian>(dst),
Endian::Big => self.inner.read_i16_into::<BigEndian>(dst),
}
}
fn read_u32_into(&mut self, dst: &mut [u32]) -> std::io::Result<()> {
match self.endian {
Endian::Little => self.inner.read_u32_into::<LittleEndian>(dst),
Endian::Big => self.inner.read_u32_into::<BigEndian>(dst),
}
}
fn read_i32_into(&mut self, dst: &mut [i32]) -> std::io::Result<()> {
match self.endian {
Endian::Little => self.inner.read_i32_into::<LittleEndian>(dst),
Endian::Big => self.inner.read_i32_into::<BigEndian>(dst),
}
}
fn read_u64_into(&mut self, dst: &mut [u64]) -> std::io::Result<()> {
match self.endian {
Endian::Little => self.inner.read_u64_into::<LittleEndian>(dst),
Endian::Big => self.inner.read_u64_into::<BigEndian>(dst),
}
}
fn read_i64_into(&mut self, dst: &mut [i64]) -> std::io::Result<()> {
match self.endian {
Endian::Little => self.inner.read_i64_into::<LittleEndian>(dst),
Endian::Big => self.inner.read_i64_into::<BigEndian>(dst),
}
}
fn read_f32_into(&mut self, dst: &mut [f32]) -> std::io::Result<()> {
match self.endian {
Endian::Little => self.inner.read_f32_into::<LittleEndian>(dst),
Endian::Big => self.inner.read_f32_into::<BigEndian>(dst),
}
}
fn read_f64_into(&mut self, dst: &mut [f64]) -> std::io::Result<()> {
match self.endian {
Endian::Little => self.inner.read_f64_into::<LittleEndian>(dst),
Endian::Big => self.inner.read_f64_into::<BigEndian>(dst),
}
}
}
File diff suppressed because it is too large Load Diff
+412
View File
@@ -0,0 +1,412 @@
// SPDX-License-Identifier: MIT
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
use std::{
collections::BTreeMap,
io::{Seek, SeekFrom, Write},
};
use byteorder::{LittleEndian, NativeEndian, WriteBytesExt};
use image::EncodableLayout;
use rayon::prelude::*;
use weezl::LzwError;
use crate::{
exif::Exif,
imgop::Dim2,
tags::{ExifGpsTag, ExifTag, TiffTag},
};
use super::{Entry, Result, TIFF_MAGIC, TiffError, Value};
pub struct TiffWriter<W> {
ifd_location: u64,
pub writer: W,
}
impl<W> TiffWriter<W>
where
W: Write + Seek,
{
pub fn new(writer: W) -> Result<Self> {
let mut tmp = Self { writer, ifd_location: 0 };
tmp.write_header()?;
Ok(tmp)
}
pub fn new_directory(&self) -> DirectoryWriter {
DirectoryWriter::new()
}
fn write_header(&mut self) -> Result<()> {
#[cfg(target_endian = "little")]
let boi: u8 = 0x49;
#[cfg(not(target_endian = "little"))]
let boi: u8 = 0x4d;
self.writer.write_all(&[boi, boi])?;
self.writer.write_u16::<NativeEndian>(TIFF_MAGIC)?;
self.ifd_location = self.writer.stream_position()?;
self.writer.write_u32::<NativeEndian>(0_u32)?;
Ok(())
}
pub fn write_strips_lzw(&mut self, data: &[u16], cpp: usize, dim: Dim2, mut strip_lines: usize) -> Result<(u32, Vec<(u32, u32)>)> {
if strip_lines == 0 {
if dim.h > 256 {
strip_lines = 256;
} else {
strip_lines = dim.h; // Use single strip
}
}
let mut tag_data = Vec::with_capacity(dim.h / strip_lines + 1);
let strips = data
.par_chunks(cpp * dim.w * strip_lines)
.map(|strip| {
let mut encoder = weezl::encode::Encoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
encoder.encode(strip.as_bytes())
})
.collect::<std::result::Result<Vec<Vec<u8>>, LzwError>>()
.map_err(|err| TiffError::General(err.to_string()))?;
for strip in strips {
let offset = self.write_data(&strip)?;
let bytes = strip.len() as u32;
tag_data.push((offset, bytes));
}
Ok((strip_lines as u32, tag_data))
}
pub fn write_data(&mut self, data: &[u8]) -> Result<u32>
where
W: Seek + Write,
{
self.pad_word_boundary()?;
let offset = self.position()?;
self.writer.write_all(data)?;
Ok(offset)
}
pub fn write_data_u16_le(&mut self, data: &[u16]) -> Result<u32>
where
W: Seek + Write,
{
self.pad_word_boundary()?;
let offset = self.position()?;
for v in data {
self.writer.write_u16::<LittleEndian>(*v)?;
}
Ok(offset)
}
pub fn write_data_f32_le(&mut self, data: &[f32]) -> Result<u32>
where
W: Seek + Write,
{
self.pad_word_boundary()?;
let offset = self.position()?;
for v in data {
self.writer.write_f32::<LittleEndian>(*v)?;
}
Ok(offset)
}
pub(crate) fn pad_word_boundary(&mut self) -> Result<()> {
if self.position()? % 4 != 0 {
let padding = [0, 0, 0];
let padd_len = 4 - (self.position()? % 4);
self.writer.write_all(&padding[..padd_len as usize])?;
}
Ok(())
}
pub fn build(mut self, root_ifd: DirectoryWriter) -> Result<()> {
let ifd0_offset = root_ifd.build(&mut self)?;
self.writer.seek(SeekFrom::Start(self.ifd_location))?;
self.writer.write_u32::<NativeEndian>(ifd0_offset)?;
Ok(())
}
}
impl<W> TiffWriter<W>
where
W: Seek,
{
pub fn position(&mut self) -> Result<u32> {
Ok(self.writer.stream_position().map(|v| v as u32)?) // TODO: try_from?
}
}
#[derive(Default)]
pub struct DirectoryWriter {
// We use BTreeMap to make sure tags are written in correct order
entries: BTreeMap<u16, Entry>,
next_ifd: u32,
}
impl DirectoryWriter {
pub fn remove_tag<T: TiffTag>(&mut self, tag: T) {
let tag: u16 = tag.into();
self.entries.remove(&tag);
}
pub fn add_tag<T: TiffTag, V: Into<Value>>(&mut self, tag: T, value: V) {
let tag: u16 = tag.into();
self.entries.insert(
tag,
Entry {
tag,
value: value.into(),
embedded: None,
},
);
}
pub fn add_untyped_tag<V: Into<Value>>(&mut self, tag: u16, value: V) {
self.entries.insert(
tag,
Entry {
tag,
value: value.into(),
embedded: None,
},
);
}
pub fn contains<T: TiffTag>(&self, tag: T) -> bool {
self.entries.contains_key(&tag.into())
}
pub fn copy<'a>(&mut self, iter: impl Iterator<Item = (&'a u16, &'a Value)>) {
for entry in iter {
if !self.entries.contains_key(entry.0) {
self.add_untyped_tag(*entry.0, entry.1.clone());
}
}
}
pub fn copy_with_override<'a>(&mut self, iter: impl Iterator<Item = (&'a u16, &'a Value)>) {
for entry in iter {
self.add_untyped_tag(*entry.0, entry.1.clone());
}
}
pub fn add_tag_undefined<T: TiffTag>(&mut self, tag: T, data: Vec<u8>) {
let tag: u16 = tag.into();
//let data = data.as_ref();
//let offset = self.write_data(data)?;
self.entries.insert(
tag,
Entry {
tag,
value: Value::Undefined(data),
embedded: None,
},
);
}
pub fn add_value<T: TiffTag>(&mut self, tag: T, value: Value) {
let tag: u16 = tag.into();
self.entries.insert(tag, Entry { tag, value, embedded: None });
}
pub fn entry_count(&self) -> u16 {
self.entries.len() as u16
}
pub fn new() -> Self {
Self {
entries: BTreeMap::new(),
next_ifd: 0,
}
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn build<W>(mut self, tiff: &mut TiffWriter<W>) -> Result<u32>
where
W: Seek + Write,
{
if self.entries.is_empty() {
return Err(TiffError::General("IFD is empty, not allowed by TIFF specification".to_string()));
}
for &mut Entry {
ref mut value,
ref mut embedded,
ref tag,
} in self.entries.values_mut()
{
let data_bytes = 4;
if value.byte_size() > data_bytes {
tiff.pad_word_boundary()?;
let offset = tiff.position()?;
value.write(&mut tiff.writer)?;
embedded.replace(offset as u32);
} else {
if value.count() == 0 {
panic!("TIFF value is empty, tag: {:?}", tag);
}
embedded.replace(value.as_embedded()?);
}
}
tiff.pad_word_boundary()?;
let offset = tiff.position()?;
tiff.writer.write_all(&self.entry_count().to_ne_bytes())?;
for (tag, entry) in self.entries {
tiff.writer.write_u16::<NativeEndian>(tag)?;
tiff.writer.write_u16::<NativeEndian>(entry.value_type())?;
tiff.writer.write_u32::<NativeEndian>(entry.count())?;
tiff
.writer
.write_u32::<NativeEndian>(entry.embedded.expect("embedded attribute must contain a value"))?;
}
tiff.writer.write_u32::<NativeEndian>(self.next_ifd)?; // Next IFD
Ok(offset)
}
/*
pub fn add_entry(&mut self, entry: Entry) {
self.ifd.insert(tag.into(), entry);
}
*/
}
impl crate::decoders::RawMetadata {
pub fn write_exif_tags<W>(&self, tiff: &mut TiffWriter<W>, root_ifd: &mut DirectoryWriter, exif_ifd: &mut DirectoryWriter) -> crate::formats::tiff::Result<()>
where
W: Write + Seek,
{
self.fill_exif_root(tiff, root_ifd)?;
Self::fill_exif_ifd(&self.exif, exif_ifd)?;
Ok(())
}
pub fn fill_exif_ifd(exif: &Exif, exif_ifd: &mut DirectoryWriter) -> Result<()> {
transfer_entry(exif_ifd, ExifTag::FNumber, &exif.fnumber)?;
transfer_entry(exif_ifd, ExifTag::ApertureValue, &exif.aperture_value)?;
transfer_entry(exif_ifd, ExifTag::BrightnessValue, &exif.brightness_value)?;
transfer_entry(exif_ifd, ExifTag::ExposureBiasValue, &exif.exposure_bias)?;
transfer_entry(exif_ifd, ExifTag::RecommendedExposureIndex, &exif.recommended_exposure_index)?;
transfer_entry(exif_ifd, ExifTag::ExposureTime, &exif.exposure_time)?;
transfer_entry(exif_ifd, ExifTag::ISOSpeedRatings, &exif.iso_speed_ratings)?;
transfer_entry(exif_ifd, ExifTag::ISOSpeed, &exif.iso_speed)?;
transfer_entry(exif_ifd, ExifTag::SensitivityType, &exif.sensitivity_type)?;
transfer_entry(exif_ifd, ExifTag::ExposureProgram, &exif.exposure_program)?;
transfer_entry(exif_ifd, ExifTag::TimeZoneOffset, &exif.timezone_offset)?;
transfer_entry(exif_ifd, ExifTag::DateTimeOriginal, &exif.date_time_original)?;
transfer_entry(exif_ifd, ExifTag::CreateDate, &exif.create_date)?;
transfer_entry(exif_ifd, ExifTag::OffsetTime, &exif.offset_time)?;
transfer_entry(exif_ifd, ExifTag::OffsetTimeOriginal, &exif.offset_time_original)?;
transfer_entry(exif_ifd, ExifTag::OffsetTimeDigitized, &exif.offset_time_digitized)?;
transfer_entry(exif_ifd, ExifTag::SubSecTime, &exif.sub_sec_time)?;
transfer_entry(exif_ifd, ExifTag::SubSecTimeOriginal, &exif.sub_sec_time_original)?;
transfer_entry(exif_ifd, ExifTag::SubSecTimeDigitized, &exif.sub_sec_time_digitized)?;
transfer_entry(exif_ifd, ExifTag::ShutterSpeedValue, &exif.shutter_speed_value)?;
transfer_entry(exif_ifd, ExifTag::MaxApertureValue, &exif.max_aperture_value)?;
transfer_entry(exif_ifd, ExifTag::SubjectDistance, &exif.subject_distance)?;
transfer_entry(exif_ifd, ExifTag::MeteringMode, &exif.metering_mode)?;
transfer_entry(exif_ifd, ExifTag::LightSource, &exif.light_source)?;
transfer_entry(exif_ifd, ExifTag::Flash, &exif.flash)?;
transfer_entry(exif_ifd, ExifTag::FocalLength, &exif.focal_length)?;
transfer_entry(exif_ifd, ExifTag::ImageNumber, &exif.image_number)?;
transfer_entry(exif_ifd, ExifTag::ColorSpace, &exif.color_space)?;
transfer_entry(exif_ifd, ExifTag::FlashEnergy, &exif.flash_energy)?;
transfer_entry(exif_ifd, ExifTag::ExposureMode, &exif.exposure_mode)?;
transfer_entry(exif_ifd, ExifTag::WhiteBalance, &exif.white_balance)?;
transfer_entry(exif_ifd, ExifTag::SceneCaptureType, &exif.scene_capture_type)?;
transfer_entry(exif_ifd, ExifTag::SubjectDistanceRange, &exif.subject_distance_range)?;
transfer_entry(exif_ifd, ExifTag::OwnerName, &exif.owner_name)?;
transfer_entry(exif_ifd, ExifTag::SerialNumber, &exif.serial_number)?;
transfer_entry(exif_ifd, ExifTag::LensSerialNumber, &exif.lens_serial_number)?;
transfer_entry(exif_ifd, ExifTag::LensSpecification, &exif.lens_spec)?;
transfer_entry(exif_ifd, ExifTag::LensMake, &exif.lens_make)?;
transfer_entry(exif_ifd, ExifTag::LensModel, &exif.lens_model)?;
transfer_entry(exif_ifd, ExifTag::UserComment, &exif.user_comment)?;
//transfer_entry(exif_ifd, ExifTag::MakerNotes, &exif.makernotes.as_ref().map(|x| Value::Undefined(x.clone())))?;
Ok(())
}
fn fill_exif_root<W>(&self, tiff: &mut TiffWriter<W>, root_ifd: &mut DirectoryWriter) -> Result<()>
where
W: Write + Seek,
{
transfer_entry(root_ifd, ExifTag::Orientation, &self.exif.orientation)?;
transfer_entry(root_ifd, ExifTag::ModifyDate, &self.exif.modify_date)?;
transfer_entry(root_ifd, ExifTag::Copyright, &self.exif.copyright)?;
transfer_entry(root_ifd, ExifTag::Artist, &self.exif.artist)?;
if let Some(gps) = &self.exif.gps {
let gps_offset = {
let mut gps_ifd = DirectoryWriter::new();
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSVersionID, &gps.gps_version_id)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSLatitudeRef, &gps.gps_latitude_ref)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSLatitude, &gps.gps_latitude)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSLongitudeRef, &gps.gps_longitude_ref)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSLongitude, &gps.gps_longitude)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSAltitudeRef, &gps.gps_altitude_ref)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSAltitude, &gps.gps_altitude)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSTimeStamp, &gps.gps_timestamp)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSSatellites, &gps.gps_satellites)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSStatus, &gps.gps_status)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSMeasureMode, &gps.gps_measure_mode)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDOP, &gps.gps_dop)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSSpeedRef, &gps.gps_speed_ref)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSSpeed, &gps.gps_speed)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSTrackRef, &gps.gps_track_ref)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSTrack, &gps.gps_track)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSImgDirectionRef, &gps.gps_img_direction_ref)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSImgDirection, &gps.gps_img_direction)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSMapDatum, &gps.gps_map_datum)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDestLatitudeRef, &gps.gps_dest_latitude_ref)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDestLatitude, &gps.gps_dest_latitude)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDestLongitudeRef, &gps.gps_dest_longitude_ref)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDestLongitude, &gps.gps_dest_longitude)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDestBearingRef, &gps.gps_dest_bearing_ref)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDestBearing, &gps.gps_dest_bearing)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDestDistanceRef, &gps.gps_dest_distance_ref)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDestDistance, &gps.gps_dest_distance)?;
transfer_entry_undefined(&mut gps_ifd, ExifGpsTag::GPSProcessingMethod, &gps.gps_processing_method)?;
transfer_entry_undefined(&mut gps_ifd, ExifGpsTag::GPSAreaInformation, &gps.gps_area_information)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDateStamp, &gps.gps_date_stamp)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSDifferential, &gps.gps_differential)?;
transfer_entry(&mut gps_ifd, ExifGpsTag::GPSHPositioningError, &gps.gps_h_positioning_error)?;
if gps_ifd.entry_count() > 0 { Some(gps_ifd.build(tiff)?) } else { None }
};
if let Some(gps_offset) = gps_offset {
root_ifd.add_tag(ExifTag::GPSInfo, [gps_offset]);
}
}
Ok(())
}
}
pub(crate) fn transfer_entry<T, V>(ifd: &mut DirectoryWriter, tag: T, entry: &Option<V>) -> Result<()>
where
T: TiffTag,
V: Into<Value> + Clone,
{
if let Some(entry) = entry {
ifd.add_tag(tag, entry.clone());
}
Ok(())
}
pub(crate) fn transfer_entry_undefined<T>(ifd: &mut DirectoryWriter, tag: T, entry: &Option<Vec<u8>>) -> Result<()>
where
T: TiffTag,
{
if let Some(entry) = entry {
ifd.add_tag_undefined(tag, entry.clone());
}
Ok(())
}