1use crate::format::{MediaType, PixelFormat, SampleFormat};
4use crate::rational::Rational;
5use crate::time::TimeBase;
6
7#[derive(Clone, Debug, PartialEq, Eq, Hash)]
10pub struct CodecId(pub String);
11
12impl CodecId {
13 pub fn new(s: impl Into<String>) -> Self {
14 Self(s.into())
15 }
16
17 pub fn as_str(&self) -> &str {
18 &self.0
19 }
20}
21
22impl From<&str> for CodecId {
23 fn from(s: &str) -> Self {
24 Self(s.to_owned())
25 }
26}
27
28impl std::fmt::Display for CodecId {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 write!(f, "{}", self.0)
31 }
32}
33
34#[derive(Clone, Debug, PartialEq, Eq, Hash)]
49pub enum CodecTag {
50 Fourcc([u8; 4]),
56
57 WaveFormat(u16),
60
61 Mp4ObjectType(u8),
65
66 Matroska(String),
69}
70
71impl CodecTag {
72 pub fn fourcc(raw: &[u8; 4]) -> Self {
74 let mut out = [0u8; 4];
75 for i in 0..4 {
76 out[i] = raw[i].to_ascii_uppercase();
77 }
78 Self::Fourcc(out)
79 }
80
81 pub fn wave_format(tag: u16) -> Self {
82 Self::WaveFormat(tag)
83 }
84
85 pub fn mp4_object_type(oti: u8) -> Self {
86 Self::Mp4ObjectType(oti)
87 }
88
89 pub fn matroska(id: impl Into<String>) -> Self {
90 Self::Matroska(id.into())
91 }
92}
93
94impl std::fmt::Display for CodecTag {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 match self {
97 Self::Fourcc(fcc) => {
98 if fcc.iter().all(|b| b.is_ascii_graphic() || *b == b' ') {
100 write!(f, "fourcc({})", std::str::from_utf8(fcc).unwrap_or("????"))
101 } else {
102 write!(
103 f,
104 "fourcc(0x{:02X}{:02X}{:02X}{:02X})",
105 fcc[0], fcc[1], fcc[2], fcc[3]
106 )
107 }
108 }
109 Self::WaveFormat(t) => write!(f, "wFormatTag(0x{t:04X})"),
110 Self::Mp4ObjectType(o) => write!(f, "mp4_oti(0x{o:02X})"),
111 Self::Matroska(s) => write!(f, "matroska({s})"),
112 }
113 }
114}
115
116#[derive(Clone, Debug)]
118pub struct CodecParameters {
119 pub codec_id: CodecId,
120 pub media_type: MediaType,
121
122 pub sample_rate: Option<u32>,
124 pub channels: Option<u16>,
125 pub sample_format: Option<SampleFormat>,
126
127 pub width: Option<u32>,
129 pub height: Option<u32>,
130 pub pixel_format: Option<PixelFormat>,
131 pub frame_rate: Option<Rational>,
132
133 pub extradata: Vec<u8>,
135
136 pub bit_rate: Option<u64>,
137}
138
139impl CodecParameters {
140 pub fn audio(codec_id: CodecId) -> Self {
141 Self {
142 codec_id,
143 media_type: MediaType::Audio,
144 sample_rate: None,
145 channels: None,
146 sample_format: None,
147 width: None,
148 height: None,
149 pixel_format: None,
150 frame_rate: None,
151 extradata: Vec::new(),
152 bit_rate: None,
153 }
154 }
155
156 pub fn matches_core(&self, other: &CodecParameters) -> bool {
162 self.codec_id == other.codec_id
163 && self.sample_rate == other.sample_rate
164 && self.channels == other.channels
165 && self.sample_format == other.sample_format
166 && self.width == other.width
167 && self.height == other.height
168 && self.pixel_format == other.pixel_format
169 }
170
171 pub fn video(codec_id: CodecId) -> Self {
172 Self {
173 codec_id,
174 media_type: MediaType::Video,
175 sample_rate: None,
176 channels: None,
177 sample_format: None,
178 width: None,
179 height: None,
180 pixel_format: None,
181 frame_rate: None,
182 extradata: Vec::new(),
183 bit_rate: None,
184 }
185 }
186}
187
188#[derive(Clone, Debug)]
190pub struct StreamInfo {
191 pub index: u32,
192 pub time_base: TimeBase,
193 pub duration: Option<i64>,
194 pub start_time: Option<i64>,
195 pub params: CodecParameters,
196}
197
198#[cfg(test)]
199mod codec_tag_tests {
200 use super::*;
201
202 #[test]
203 fn fourcc_uppercases_on_construction() {
204 let t = CodecTag::fourcc(b"div3");
205 assert_eq!(t, CodecTag::Fourcc(*b"DIV3"));
206 let t2 = CodecTag::fourcc(b"MP42");
208 assert_eq!(t2, CodecTag::Fourcc(*b"MP42"));
209 let t3 = CodecTag::fourcc(&[0xFF, b'a', 0x00, b'1']);
210 assert_eq!(t3, CodecTag::Fourcc([0xFF, b'A', 0x00, b'1']));
211 }
212
213 #[test]
214 fn fourcc_equality_case_insensitive_via_ctor() {
215 assert_eq!(CodecTag::fourcc(b"xvid"), CodecTag::fourcc(b"XVID"));
216 assert_eq!(CodecTag::fourcc(b"DiV3"), CodecTag::fourcc(b"div3"));
217 }
218
219 #[test]
220 fn display_printable_fourcc() {
221 assert_eq!(CodecTag::fourcc(b"XVID").to_string(), "fourcc(XVID)");
222 }
223
224 #[test]
225 fn display_non_printable_fourcc_as_hex() {
226 let t = CodecTag::Fourcc([0x00, 0x00, 0x00, 0x01]);
227 assert_eq!(t.to_string(), "fourcc(0x00000001)");
228 }
229
230 #[test]
231 fn display_wave_format() {
232 assert_eq!(
233 CodecTag::wave_format(0x0055).to_string(),
234 "wFormatTag(0x0055)"
235 );
236 }
237
238 #[test]
239 fn display_mp4_oti() {
240 assert_eq!(CodecTag::mp4_object_type(0x40).to_string(), "mp4_oti(0x40)");
241 }
242
243 #[test]
244 fn display_matroska() {
245 assert_eq!(
246 CodecTag::matroska("V_MPEG4/ISO/AVC").to_string(),
247 "matroska(V_MPEG4/ISO/AVC)",
248 );
249 }
250}