Add PES frame types and FileSectorReader — foundation for stream refactor

- pes.rs: PesFrame, InputStream, OutputStream traits
- sector.rs: SectorReader now public, added FileSectorReader (ISO = file)
- Foundation for unified DiscStream that handles both disc and ISO
This commit is contained in:
MattJackson
2026-04-15 02:37:54 +00:00
parent 610f1b62b6
commit ccb1fadedf
3 changed files with 88 additions and 8 deletions
+9 -7
View File
@@ -79,26 +79,29 @@ pub mod keydb;
pub(crate) mod labels; pub(crate) mod labels;
pub(crate) mod mpls; pub(crate) mod mpls;
pub mod mux; pub mod mux;
pub mod pes;
pub(crate) mod platform; pub(crate) mod platform;
pub mod profile; pub mod profile;
pub mod scsi; pub mod scsi;
pub(crate) mod sector; pub mod sector;
pub(crate) mod speed; pub(crate) mod speed;
pub(crate) mod udf; pub(crate) mod udf;
pub use drive::capture::{
capture_drive_data, mask_bytes, mask_string, CapturedFeature, DriveCapture,
};
pub use drive::{find_drive, find_drives, Drive, DriveStatus}; pub use drive::{find_drive, find_drives, Drive, DriveStatus};
pub use drive::capture::{DriveCapture, CapturedFeature, capture_drive_data, mask_string, mask_bytes};
pub use error::{Error, Result}; pub use error::{Error, Result};
pub use event::{Event, EventKind}; pub use event::{Event, EventKind};
pub use identity::DriveId; pub use identity::DriveId;
pub use profile::DriveProfile; pub use profile::DriveProfile;
// Platform trait is pub(crate) -- callers use Drive, not Platform directly // Platform trait is pub(crate) -- callers use Drive, not Platform directly
pub use decrypt::{decrypt_sectors, DecryptKeys};
pub use disc::{ pub use disc::{
AacsState, AudioStream, Clip, Codec, ColorSpace, ContentFormat, ContentReader, Disc, AacsState, AudioChannels, AudioStream, Clip, Codec, ColorSpace, ContentFormat,
DiscFormat, DiscTitle, Extent, HdrFormat, KeySource, ScanOptions, Stream, SubtitleStream, Disc, DiscFormat, DiscTitle, Extent, FrameRate, HdrFormat, KeySource, Resolution, SampleRate,
VideoStream, ScanOptions, Stream, SubtitleStream, VideoStream,
}; };
pub use mux::DiscOptions;
pub use mux::DiscStream; pub use mux::DiscStream;
pub use mux::IOStream; pub use mux::IOStream;
pub use mux::IsoStream; pub use mux::IsoStream;
@@ -108,7 +111,6 @@ pub use mux::NetworkStream;
pub use mux::NullStream; pub use mux::NullStream;
pub use mux::StdioStream; pub use mux::StdioStream;
pub use mux::{open_input, open_output, parse_url, InputOptions, StreamUrl}; pub use mux::{open_input, open_output, parse_url, InputOptions, StreamUrl};
pub use decrypt::{DecryptKeys, decrypt_sectors};
pub use scsi::ScsiTransport; pub use scsi::ScsiTransport;
pub use sector::SectorReader; pub use sector::SectorReader;
pub use speed::DriveSpeed; pub use speed::DriveSpeed;
+39
View File
@@ -0,0 +1,39 @@
//! PES frame — the universal intermediate format.
//!
//! Every input stream produces PES frames. Every output stream consumes them.
//! The pipeline just moves frames: input.next_frame() → output.write_frame().
//!
//! A PES frame is one unit of elementary stream data: a video frame,
//! an audio frame, a subtitle packet. It has a track ID, timestamp,
//! and the raw codec data.
/// One frame of elementary stream data.
#[derive(Debug, Clone)]
pub struct PesFrame {
/// Track index (0-based, matches StreamInfo track order).
pub track: usize,
/// Presentation timestamp in nanoseconds.
pub pts: i64,
/// True if this is a keyframe (IDR for video).
pub keyframe: bool,
/// Raw elementary stream data (NAL units, audio samples, etc).
pub data: Vec<u8>,
}
/// Input stream — produces PES frames from any source.
pub trait InputStream {
/// Get the next frame. Returns None at end of stream.
fn next_frame(&mut self) -> std::io::Result<Option<PesFrame>>;
/// Stream metadata (tracks, duration, etc).
fn info(&self) -> &crate::disc::DiscTitle;
}
/// Output stream — consumes PES frames to any destination.
pub trait OutputStream {
/// Write one frame.
fn write_frame(&mut self, frame: &PesFrame) -> std::io::Result<()>;
/// Finalize (flush, write index, close).
fn finish(&mut self) -> std::io::Result<()>;
}
+40 -1
View File
@@ -7,8 +7,47 @@
use crate::error::Result; use crate::error::Result;
/// Read 2048-byte sectors from a disc or disc image. /// Read 2048-byte sectors from a disc or disc image.
pub trait SectorReader { pub trait SectorReader: Send {
/// Read `count` sectors starting at `lba` into `buf`. /// Read `count` sectors starting at `lba` into `buf`.
/// `buf` must be at least `count * 2048` bytes. /// `buf` must be at least `count * 2048` bytes.
fn read_sectors(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result<usize>; fn read_sectors(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result<usize>;
/// Total capacity in sectors, if known.
fn capacity(&self) -> u32 { 0 }
}
/// SectorReader backed by a file (ISO image).
/// Seeks to lba * 2048, reads count * 2048 bytes.
pub struct FileSectorReader {
file: std::io::BufReader<std::fs::File>,
capacity: u32,
}
impl FileSectorReader {
pub fn open(path: &str) -> std::io::Result<Self> {
let file = std::fs::File::open(path)?;
let len = file.metadata()?.len();
let capacity = (len / 2048) as u32;
Ok(Self {
file: std::io::BufReader::with_capacity(4 * 1024 * 1024, file),
capacity,
})
}
}
impl SectorReader for FileSectorReader {
fn read_sectors(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result<usize> {
use std::io::{Read, Seek, SeekFrom};
let offset = lba as u64 * 2048;
let bytes = count as usize * 2048;
self.file.seek(SeekFrom::Start(offset))
.map_err(|e| crate::error::Error::IoError { source: e })?;
self.file.read_exact(&mut buf[..bytes])
.map_err(|e| crate::error::Error::IoError { source: e })?;
Ok(bytes)
}
fn capacity(&self) -> u32 {
self.capacity
}
} }