Skip to main content

mediaway_container/
lib.rs

1//! Container facade — shared traits and Mediaway-typed MP4 surface.
2//!
3//! Pure ISOBMFF lives in [`iso_bmff`]. This crate maps to [`mediaway_common`]
4//! types and exposes [`Mux`] / [`Demux`] / [`DemuxDecrypt`].
5
6#![forbid(unsafe_code)]
7
8pub mod adts;
9pub mod convert;
10pub mod flv;
11pub mod mp3;
12pub mod mp4;
13mod mp4_parser;
14pub mod ogg;
15pub mod replay;
16pub mod ts;
17pub mod wav;
18#[cfg(any(feature = "mux", feature = "demux"))]
19pub mod webm;
20
21use mediaway_common::{Packet, StreamInfo};
22
23/// Pair a muxer and demuxer for one container format.
24pub trait ContainerFormat {
25    /// Muxer type (often a typestate root).
26    type Muxer;
27    /// Demuxer type.
28    type Demuxer;
29
30    /// Empty muxer (track registration / open state).
31    fn muxer() -> Self::Muxer;
32    /// Empty demuxer.
33    fn demuxer() -> Self::Demuxer;
34}
35
36/// Live mux session: packets in → container bytes out.
37pub trait Mux {
38    /// Implementation error type.
39    type Error;
40
41    /// Push one compressed packet.
42    ///
43    /// # Errors
44    ///
45    /// Returns when the packet does not match registered tracks or framing fails.
46    fn push_packet(&mut self, packet: &Packet) -> Result<(), Self::Error>;
47    /// Flush pending fragments / trailers.
48    fn flush(&mut self);
49    /// Append available container bytes into `out`; returns bytes written.
50    fn poll_bytes(&mut self, out: &mut Vec<u8>) -> usize;
51
52    /// Supply a track's codec configuration record after [`MuxOpen::begin`].
53    ///
54    /// For backends that only know their config record after encoding a frame (e.g.
55    /// `VideoToolbox`, which derives SPS/PPS internally), `StreamInfo::extra_data` is
56    /// still empty at track-registration time and only becomes real once a packet comes
57    /// out. This is the hook that lets a caller correct it.
58    ///
59    /// **Defaults to ignoring the call**, because most containers commit their track
60    /// header at `begin()` and cannot revise it afterwards — [`webm::Muxer`] is exactly
61    /// that case, since `CodecPrivate` is written into `Tracks` in the EBML header. A
62    /// container that cannot honour this drops late `extra_data` silently rather than
63    /// failing, which is a real limitation: on such a container, a late-config backend
64    /// produces a file with no codec configuration record. Pair those backends with
65    /// [`mp4::Muxer`], which buffers `moov` until the first fragment and therefore can.
66    fn set_track_extra_data(&mut self, track_id: u32, extra_data: mediaway_common::Bytes) {
67        let _ = (track_id, extra_data);
68    }
69}
70
71/// Track-registration phase of a muxer: add tracks, then [`begin`](Self::begin) into a
72/// live [`Mux`].
73///
74/// Both [`mp4::Muxer`] and [`webm::Muxer`] are typestate — tracks exist only before
75/// `begin()`, so "add a track to a running mux" is unrepresentable rather than a runtime
76/// error. This trait names that shared shape so a composition layer can be generic over
77/// the container instead of hardcoding one; see `mediaway`'s `EncodeSession`.
78///
79/// `Live: Mux<Error = Self::Error>` deliberately ties both phases to one error type. A
80/// muxer whose registration and streaming phases failed differently would need two error
81/// parameters everywhere downstream, and neither container has that shape.
82pub trait MuxOpen {
83    /// The streaming phase this becomes after [`begin`](Self::begin).
84    type Live: Mux<Error = Self::Error>;
85    /// Implementation error type, shared with [`Self::Live`].
86    type Error;
87
88    /// Lowest track id this container accepts.
89    ///
90    /// Defaults to `0`, which is what ISOBMFF allows. Matroska/`WebM` reserve `TrackNumber`
91    /// `0` and reject it outright, so [`webm::Muxer`] raises this to `1`.
92    ///
93    /// This is here because track numbering is a **container** rule, not a caller's choice.
94    /// Without it, a composition layer that hands the encoder's own default id (`0`) to the
95    /// muxer works on MP4 and fails on `WebM` for a reason that has nothing to do with the
96    /// caller's code — which is exactly what happened the first time `mediaway`'s
97    /// `EncodeSession` was pointed at a `WebM` muxer.
98    const FIRST_TRACK_ID: u32 = 0;
99
100    /// Register one track, returning its assigned track id.
101    ///
102    /// # Errors
103    ///
104    /// Returns when the track is a duplicate, or when the container has no mapping for
105    /// the stream's codec.
106    fn add_track(&mut self, track: StreamInfo) -> Result<u32, Self::Error>;
107
108    /// Finish registration and begin streaming. Consumes `self`: no track can be added
109    /// afterwards.
110    fn begin(self) -> Self::Live;
111}
112
113/// Any container's mux failure, as one type.
114///
115/// Exists so a layer generic over [`MuxOpen`] can report failures without a generic error
116/// parameter of its own — `mediaway`'s `PipelineError::Mux` wraps this, so adding a
117/// container does not change `PipelineError`'s shape.
118///
119/// **Closed by design.** A third-party [`MuxOpen`] implementation cannot convert into this,
120/// so it cannot be used with a layer that requires `Error: Into<ContainerError>`. The
121/// alternative — `Box<dyn Error>` — would make every mux failure untypeable by the caller,
122/// and this crate owns every container mediaway ships. `#[non_exhaustive]` keeps adding one
123/// non-breaking.
124#[derive(Debug, thiserror::Error)]
125#[non_exhaustive]
126pub enum ContainerError {
127    /// MP4 / fragmented-MP4 mux failure.
128    #[error("MP4 mux error: {0}")]
129    Mp4(#[from] mp4::Error),
130    /// `WebM` / Matroska mux failure.
131    #[cfg(feature = "mux")]
132    #[error("WebM mux error: {0}")]
133    Webm(#[from] webm::Error),
134}
135
136/// Demux session: container bytes in → packets out.
137pub trait Demux {
138    /// Feed container bytes (sans-io; caller owns I/O).
139    fn push_bytes(&mut self, chunk: &[u8]);
140    /// Tracks discovered so far (e.g. after `moov`).
141    fn streams(&self) -> &[StreamInfo];
142    /// Next demuxed packet, if any.
143    fn poll_packet(&mut self) -> Option<Packet>;
144}
145
146/// Optional `ClearKey` hook for demuxers that support ISO CENC.
147pub trait DemuxDecrypt: Demux {
148    /// Supply a 128-bit content key for sample decrypt.
149    fn set_decryption_key(&mut self, key: [u8; 16]);
150    /// Drop any previously set key.
151    fn clear_decryption_key(&mut self);
152}