Skip to main content

ff_format/
container.rs

1//! Container-level media information.
2//!
3//! [`ContainerInfo`] exposes the fields that belong to the media container as a
4//! whole (format name, overall bitrate, total stream count) rather than to any
5//! individual stream. Both [`crate::stream::VideoStreamInfo`] and
6//! [`crate::stream::AudioStreamInfo`] cover per-stream data; this type fills the
7//! gap with the container-level layer.
8
9/// Container-level metadata extracted from `AVFormatContext`.
10///
11/// Obtain an instance via `VideoDecoder::container_info` or
12/// `AudioDecoder::container_info` from `ff-decode`.
13///
14/// # Examples
15///
16/// ```
17/// use ff_format::ContainerInfo;
18///
19/// let info = ContainerInfo::builder()
20///     .format_name("mov,mp4,m4a,3gp,3g2,mj2")
21///     .bit_rate(2_048_000)
22///     .nb_streams(2)
23///     .build();
24///
25/// assert_eq!(info.format_name(), "mov,mp4,m4a,3gp,3g2,mj2");
26/// assert_eq!(info.bit_rate(), Some(2_048_000));
27/// assert_eq!(info.nb_streams(), 2);
28/// ```
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct ContainerInfo {
31    format_name: String,
32    bit_rate: Option<u64>,
33    nb_streams: u32,
34}
35
36impl ContainerInfo {
37    /// Returns a builder for constructing a [`ContainerInfo`].
38    #[must_use]
39    pub fn builder() -> ContainerInfoBuilder {
40        ContainerInfoBuilder::default()
41    }
42
43    /// Short format name as reported by `AVInputFormat::name`
44    /// (e.g. `"mov,mp4,m4a,3gp,3g2,mj2"`, `"matroska,webm"`, `"mp3"`).
45    ///
46    /// Returns an empty string when the format context has no associated
47    /// input format (e.g. raw streams or certain network sources).
48    #[must_use]
49    pub fn format_name(&self) -> &str {
50        &self.format_name
51    }
52
53    /// Container-level bitrate in bits per second, or `None` when the
54    /// container does not report one (live streams, raw formats, etc.).
55    #[must_use]
56    pub fn bit_rate(&self) -> Option<u64> {
57        self.bit_rate
58    }
59
60    /// Total number of streams in the container (video + audio + subtitle + …).
61    #[must_use]
62    pub fn nb_streams(&self) -> u32 {
63        self.nb_streams
64    }
65}
66
67/// Builder for [`ContainerInfo`].
68#[derive(Debug, Default)]
69pub struct ContainerInfoBuilder {
70    format_name: String,
71    bit_rate: Option<u64>,
72    nb_streams: u32,
73}
74
75impl ContainerInfoBuilder {
76    /// Sets the short format name (e.g. `"mp3"`, `"mov,mp4,m4a,3gp,3g2,mj2"`).
77    #[must_use]
78    pub fn format_name(mut self, name: impl Into<String>) -> Self {
79        self.format_name = name.into();
80        self
81    }
82
83    /// Sets the container-level bitrate in bits per second.
84    #[must_use]
85    pub fn bit_rate(mut self, br: u64) -> Self {
86        self.bit_rate = Some(br);
87        self
88    }
89
90    /// Sets the total number of streams.
91    #[must_use]
92    pub fn nb_streams(mut self, n: u32) -> Self {
93        self.nb_streams = n;
94        self
95    }
96
97    /// Builds the [`ContainerInfo`].
98    #[must_use]
99    pub fn build(self) -> ContainerInfo {
100        ContainerInfo {
101            format_name: self.format_name,
102            bit_rate: self.bit_rate,
103            nb_streams: self.nb_streams,
104        }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn container_info_builder_sets_fields() {
114        let info = ContainerInfo::builder()
115            .format_name("mp4")
116            .bit_rate(1_000_000)
117            .nb_streams(3)
118            .build();
119
120        assert_eq!(info.format_name(), "mp4");
121        assert_eq!(info.bit_rate(), Some(1_000_000));
122        assert_eq!(info.nb_streams(), 3);
123    }
124
125    #[test]
126    fn bit_rate_none_when_not_set() {
127        let info = ContainerInfo::builder().format_name("mp3").build();
128        assert_eq!(info.bit_rate(), None);
129    }
130
131    #[test]
132    fn default_builder_produces_empty_info() {
133        let info = ContainerInfo::builder().build();
134        assert_eq!(info.format_name(), "");
135        assert_eq!(info.bit_rate(), None);
136        assert_eq!(info.nb_streams(), 0);
137    }
138}