mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-25 15:39:51 +00:00
efa8167962
- crates/mxf (L0): KLV walk, partitions, primer, header metadata, index tables, material ->
file package resolution, AVC/VC-3/ProRes/MPEG-2 identification, PCM/AES3 (ST 331) sound,
timecode; OP1a and OP-Atom (clip wrapping).
- crates/ogg (L0): RFC 3533 pages/CRC/packets, RFC 7845 Opus timing (granules, pre-skip, end trim).
- codecs: MxfSource (GOP cache, POC ordering for AVC without temporal offsets, MPEG-2 reported
unsupported), OggSource (Opus with pre-roll seeking, Vorbis), Annex B H.264 decoder.
- media: image sequences (numbered stills, missing frames held, frame cache); BWF bext/iXML
TimeReference -> start timecode.
- engine/UI: file.import {imageSequence}, auto-detect setting, file.importImageSequence,
Media Browser "Import as Image Sequence"; CLI probe details for MXF/Ogg/BWF/sequences.
- Oracle tests vs ffmpeg for MXF, Ogg, image sequences, BWF; truncation/mutation tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
69 lines
2.0 KiB
Rust
69 lines
2.0 KiB
Rust
//! Random-access byte sources.
|
|
|
|
use std::io;
|
|
use std::sync::Arc;
|
|
|
|
/// Random-access, read-only byte source (file, in-memory buffer, web Blob…). Same shape as the
|
|
/// other FilmCraft demuxers' `ByteSource`, so one adapter serves all of them.
|
|
pub trait ByteSource {
|
|
/// Total length in bytes.
|
|
fn len(&self) -> u64;
|
|
/// Fill `buf` entirely from `offset`; `UnexpectedEof` past the end.
|
|
fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()>;
|
|
/// True if the source holds no bytes.
|
|
fn is_empty(&self) -> bool {
|
|
self.len() == 0
|
|
}
|
|
}
|
|
|
|
fn eof() -> io::Error {
|
|
io::Error::new(io::ErrorKind::UnexpectedEof, "read past end of byte source")
|
|
}
|
|
|
|
impl ByteSource for [u8] {
|
|
fn len(&self) -> u64 {
|
|
<[u8]>::len(self) as u64
|
|
}
|
|
fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> {
|
|
let start = usize::try_from(offset).map_err(|_| eof())?;
|
|
let end = start.checked_add(buf.len()).ok_or_else(eof)?;
|
|
buf.copy_from_slice(self.get(start..end).ok_or_else(eof)?);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl ByteSource for Vec<u8> {
|
|
fn len(&self) -> u64 {
|
|
self.as_slice().len() as u64
|
|
}
|
|
fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> {
|
|
self.as_slice().read_at(offset, buf)
|
|
}
|
|
}
|
|
|
|
impl<T: ByteSource + ?Sized> ByteSource for &T {
|
|
fn len(&self) -> u64 {
|
|
(**self).len()
|
|
}
|
|
fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> {
|
|
(**self).read_at(offset, buf)
|
|
}
|
|
}
|
|
|
|
impl<T: ByteSource + ?Sized> ByteSource for Arc<T> {
|
|
fn len(&self) -> u64 {
|
|
(**self).len()
|
|
}
|
|
fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> {
|
|
(**self).read_at(offset, buf)
|
|
}
|
|
}
|
|
|
|
/// Up to `len` bytes at `offset` (fewer at the end of the source).
|
|
pub(crate) fn read_upto(src: &(impl ByteSource + ?Sized), offset: u64, len: usize) -> io::Result<Vec<u8>> {
|
|
let n = src.len().saturating_sub(offset).min(len as u64) as usize;
|
|
let mut v = vec![0u8; n];
|
|
src.read_at(offset, &mut v)?;
|
|
Ok(v)
|
|
}
|