Skip to main content

wedeo_format/
context.rs

1use tracing::debug;
2use wedeo_core::error::{Error, Result};
3use wedeo_core::metadata::Metadata;
4use wedeo_core::packet::Packet;
5
6use crate::demuxer::{Demuxer, ProbeData, SeekFlags, Stream};
7use crate::io::{BufferedIo, FileIo};
8use crate::muxer::Muxer;
9use crate::registry;
10
11/// High-level input context — wraps demuxer + I/O for easy demuxing.
12pub struct InputContext {
13    demuxer: Box<dyn Demuxer>,
14    io: BufferedIo,
15    pub streams: Vec<Stream>,
16    pub metadata: Metadata,
17    pub duration: i64,
18    pub start_time: i64,
19}
20
21impl InputContext {
22    /// Open a file and auto-detect the format.
23    pub fn open(path: &str) -> Result<Self> {
24        let file_io = FileIo::open(path)?;
25        let mut io = BufferedIo::new(Box::new(file_io));
26
27        // Read probe data (up to 4096 bytes, file may be smaller)
28        let file_size = io.size().unwrap_or(4096);
29        let probe_size = file_size.min(4096) as usize;
30        let probe_buf = io.read_bytes(probe_size)?;
31        io.seek(0)?;
32
33        let probe_data = ProbeData {
34            filename: path,
35            buf: &probe_buf,
36        };
37
38        let factory = registry::probe(&probe_data).ok_or(Error::DemuxerNotFound)?;
39
40        let mut demuxer = factory.create()?;
41        let header = demuxer.read_header(&mut io)?;
42
43        let format_name = factory.descriptor().name;
44        debug!(
45            path,
46            format = format_name,
47            streams = header.streams.len(),
48            "InputContext opened"
49        );
50
51        Ok(Self {
52            demuxer,
53            io,
54            streams: header.streams,
55            metadata: header.metadata,
56            duration: header.duration,
57            start_time: header.start_time,
58        })
59    }
60
61    /// Read the next packet.
62    pub fn read_packet(&mut self) -> Result<Packet> {
63        self.demuxer.read_packet(&mut self.io)
64    }
65
66    /// Seek to a timestamp in the specified stream.
67    pub fn seek(&mut self, stream_index: usize, timestamp: i64, flags: SeekFlags) -> Result<()> {
68        self.demuxer
69            .seek(&mut self.io, stream_index, timestamp, flags)
70    }
71
72    /// Get a stream by index.
73    pub fn stream(&self, index: usize) -> Option<&Stream> {
74        self.streams.get(index)
75    }
76
77    /// Number of streams.
78    pub fn nb_streams(&self) -> usize {
79        self.streams.len()
80    }
81}
82
83/// High-level output context — wraps muxer + I/O for easy muxing.
84pub struct OutputContext {
85    muxer: Box<dyn Muxer>,
86    io: BufferedIo,
87}
88
89impl OutputContext {
90    /// Create an output file with the specified format.
91    pub fn create(path: &str, format_name: &str, streams: &[Stream]) -> Result<Self> {
92        let factory = registry::find_muxer_by_name(format_name).ok_or(Error::MuxerNotFound)?;
93        let mut muxer = factory.create()?;
94        let file_io = FileIo::create(path)?;
95        let mut io = BufferedIo::new(Box::new(file_io));
96        muxer.write_header(&mut io, streams)?;
97        Ok(Self { muxer, io })
98    }
99
100    /// Write a single packet.
101    pub fn write_packet(&mut self, packet: &Packet) -> Result<()> {
102        self.muxer.write_packet(&mut self.io, packet)
103    }
104
105    /// Finalize the output: write trailer and flush.
106    pub fn finish(mut self) -> Result<()> {
107        self.muxer.write_trailer(&mut self.io)?;
108        self.io.flush()
109    }
110}