pub mod binary;
pub mod links;
pub mod version;
use crate::blocks::*;
use crate::error::{Mf4Error, Result};
use crate::io::ByteSource;
pub use version::Mf4Version;
pub fn parse_block_header<S: ByteSource>(source: &S, offset: u64) -> Result<BlockHeader> {
let data = source.read_bytes(offset, BLOCK_HEADER_SIZE)?;
let header = BlockHeader::parse(&data, offset)?;
let end = offset.saturating_add(header.length);
if end > source.len() {
return Err(Mf4Error::truncated(
offset,
header.length.min(usize::MAX as u64) as usize,
source.len().saturating_sub(offset) as usize,
));
}
Ok(header)
}
pub fn parse_id_block<S: ByteSource>(source: &S) -> Result<IdBlock> {
let data = source.read_bytes(0, ID_BLOCK_SIZE)?;
IdBlock::parse(&data)
}
pub fn parse_hd_block<S: ByteSource>(source: &S, offset: u64) -> Result<HdBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
HdBlock::parse(&data, offset)
}
pub fn parse_fh_block<S: ByteSource>(source: &S, offset: u64) -> Result<FhBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
FhBlock::parse(&data, offset)
}
pub fn parse_dg_block<S: ByteSource>(source: &S, offset: u64) -> Result<DgBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
DgBlock::parse(&data, offset)
}
pub fn parse_cg_block<S: ByteSource>(source: &S, offset: u64) -> Result<CgBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
CgBlock::parse(&data, offset)
}
pub fn parse_cn_block<S: ByteSource>(source: &S, offset: u64) -> Result<CnBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
CnBlock::parse(&data, offset)
}
pub fn parse_cc_block<S: ByteSource>(source: &S, offset: u64) -> Result<CcBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
CcBlock::parse(&data, offset)
}
pub fn parse_text_block<S: ByteSource>(source: &S, offset: u64) -> Result<TextOrMetadata> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
TextOrMetadata::parse(&data, offset)
}
pub fn read_text<S: ByteSource>(source: &S, link: u64) -> Result<String> {
if link == 0 {
return Ok(String::new());
}
let block = parse_text_block(source, link)?;
Ok(block.comment())
}
pub fn read_metadata<S: ByteSource>(
source: &S,
link: u64,
) -> Result<Option<crate::model::Metadata>> {
if link == 0 {
return Ok(None);
}
match parse_text_block(source, link)? {
TextOrMetadata::Metadata(md) => Ok(Some(md.metadata())),
TextOrMetadata::Text(_) => Ok(None),
}
}
pub fn parse_si_block<S: ByteSource>(source: &S, offset: u64) -> Result<SiBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
SiBlock::parse(&data, offset)
}
pub fn parse_data_block<S: ByteSource>(source: &S, offset: u64) -> Result<DataBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
DataBlock::parse(&data, offset)
}
pub fn parse_ca_block<S: ByteSource>(source: &S, offset: u64) -> Result<CaBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
CaBlock::parse(&data, offset)
}
pub fn parse_at_block<S: ByteSource>(source: &S, offset: u64) -> Result<AtBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
AtBlock::parse(&data, offset)
}
pub fn parse_ev_block<S: ByteSource>(source: &S, offset: u64) -> Result<EvBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
EvBlock::parse(&data, offset)
}
pub fn parse_ch_block<S: ByteSource>(source: &S, offset: u64) -> Result<ChBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
ChBlock::parse(&data, offset)
}
pub fn parse_sr_block<S: ByteSource>(source: &S, offset: u64) -> Result<SrBlock> {
let header = parse_block_header(source, offset)?;
let data = source.read_bytes(offset, header.length as usize)?;
SrBlock::parse(&data, offset)
}
pub struct LinkedBlockIterator<'a, S: ByteSource, T, F>
where
F: Fn(&S, u64) -> Result<T>,
{
source: &'a S,
next_offset: u64,
parse_fn: F,
get_next: fn(&T) -> u64,
}
impl<'a, S: ByteSource, T, F> Iterator for LinkedBlockIterator<'a, S, T, F>
where
F: Fn(&S, u64) -> Result<T>,
{
type Item = Result<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.next_offset == 0 {
return None;
}
match (self.parse_fn)(self.source, self.next_offset) {
Ok(block) => {
self.next_offset = (self.get_next)(&block);
Some(Ok(block))
}
Err(e) => {
self.next_offset = 0; Some(Err(e))
}
}
}
}
pub fn iter_data_groups<S: ByteSource>(
source: &S,
first_dg: u64,
) -> impl Iterator<Item = Result<DgBlock>> + '_ {
LinkedBlockIterator {
source,
next_offset: first_dg,
parse_fn: parse_dg_block,
get_next: |dg: &DgBlock| dg.dg_next,
}
}
pub fn iter_channel_groups<S: ByteSource>(
source: &S,
first_cg: u64,
) -> impl Iterator<Item = Result<CgBlock>> + '_ {
LinkedBlockIterator {
source,
next_offset: first_cg,
parse_fn: parse_cg_block,
get_next: |cg: &CgBlock| cg.cg_next,
}
}
pub fn iter_channels<S: ByteSource>(
source: &S,
first_cn: u64,
) -> impl Iterator<Item = Result<CnBlock>> + '_ {
LinkedBlockIterator {
source,
next_offset: first_cn,
parse_fn: parse_cn_block,
get_next: |cn: &CnBlock| cn.cn_next,
}
}
pub fn iter_attachments<S: ByteSource>(
source: &S,
first_at: u64,
) -> impl Iterator<Item = Result<AtBlock>> + '_ {
LinkedBlockIterator {
source,
next_offset: first_at,
parse_fn: parse_at_block,
get_next: |at: &AtBlock| at.at_next,
}
}
pub fn iter_events<S: ByteSource>(
source: &S,
first_ev: u64,
) -> impl Iterator<Item = Result<EvBlock>> + '_ {
LinkedBlockIterator {
source,
next_offset: first_ev,
parse_fn: parse_ev_block,
get_next: |ev: &EvBlock| ev.ev_next,
}
}
pub fn iter_hierarchy<S: ByteSource>(
source: &S,
first_ch: u64,
) -> impl Iterator<Item = Result<ChBlock>> + '_ {
LinkedBlockIterator {
source,
next_offset: first_ch,
parse_fn: parse_ch_block,
get_next: |ch: &ChBlock| ch.ch_next,
}
}
pub fn iter_sample_reduction<S: ByteSource>(
source: &S,
first_sr: u64,
) -> impl Iterator<Item = Result<SrBlock>> + '_ {
LinkedBlockIterator {
source,
next_offset: first_sr,
parse_fn: parse_sr_block,
get_next: |sr: &SrBlock| sr.sr_next,
}
}