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}