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
116pub trait CodecResolver: Sync {
127 fn resolve_tag(&self, tag: &CodecTag, probe_data: Option<&[u8]>) -> Option<CodecId>;
128}
129
130#[derive(Default, Clone, Copy)]
134pub struct NullCodecResolver;
135
136impl CodecResolver for NullCodecResolver {
137 fn resolve_tag(&self, _tag: &CodecTag, _probe_data: Option<&[u8]>) -> Option<CodecId> {
138 None
139 }
140}
141
142#[derive(Clone, Debug)]
144pub struct CodecParameters {
145 pub codec_id: CodecId,
146 pub media_type: MediaType,
147
148 pub sample_rate: Option<u32>,
150 pub channels: Option<u16>,
151 pub sample_format: Option<SampleFormat>,
152
153 pub width: Option<u32>,
155 pub height: Option<u32>,
156 pub pixel_format: Option<PixelFormat>,
157 pub frame_rate: Option<Rational>,
158
159 pub extradata: Vec<u8>,
161
162 pub bit_rate: Option<u64>,
163}
164
165impl CodecParameters {
166 pub fn audio(codec_id: CodecId) -> Self {
167 Self {
168 codec_id,
169 media_type: MediaType::Audio,
170 sample_rate: None,
171 channels: None,
172 sample_format: None,
173 width: None,
174 height: None,
175 pixel_format: None,
176 frame_rate: None,
177 extradata: Vec::new(),
178 bit_rate: None,
179 }
180 }
181
182 pub fn matches_core(&self, other: &CodecParameters) -> bool {
188 self.codec_id == other.codec_id
189 && self.sample_rate == other.sample_rate
190 && self.channels == other.channels
191 && self.sample_format == other.sample_format
192 && self.width == other.width
193 && self.height == other.height
194 && self.pixel_format == other.pixel_format
195 }
196
197 pub fn video(codec_id: CodecId) -> Self {
198 Self {
199 codec_id,
200 media_type: MediaType::Video,
201 sample_rate: None,
202 channels: None,
203 sample_format: None,
204 width: None,
205 height: None,
206 pixel_format: None,
207 frame_rate: None,
208 extradata: Vec::new(),
209 bit_rate: None,
210 }
211 }
212}
213
214#[derive(Clone, Debug)]
216pub struct StreamInfo {
217 pub index: u32,
218 pub time_base: TimeBase,
219 pub duration: Option<i64>,
220 pub start_time: Option<i64>,
221 pub params: CodecParameters,
222}
223
224#[cfg(test)]
225mod codec_tag_tests {
226 use super::*;
227
228 #[test]
229 fn fourcc_uppercases_on_construction() {
230 let t = CodecTag::fourcc(b"div3");
231 assert_eq!(t, CodecTag::Fourcc(*b"DIV3"));
232 let t2 = CodecTag::fourcc(b"MP42");
234 assert_eq!(t2, CodecTag::Fourcc(*b"MP42"));
235 let t3 = CodecTag::fourcc(&[0xFF, b'a', 0x00, b'1']);
236 assert_eq!(t3, CodecTag::Fourcc([0xFF, b'A', 0x00, b'1']));
237 }
238
239 #[test]
240 fn fourcc_equality_case_insensitive_via_ctor() {
241 assert_eq!(CodecTag::fourcc(b"xvid"), CodecTag::fourcc(b"XVID"));
242 assert_eq!(CodecTag::fourcc(b"DiV3"), CodecTag::fourcc(b"div3"));
243 }
244
245 #[test]
246 fn display_printable_fourcc() {
247 assert_eq!(CodecTag::fourcc(b"XVID").to_string(), "fourcc(XVID)");
248 }
249
250 #[test]
251 fn display_non_printable_fourcc_as_hex() {
252 let t = CodecTag::Fourcc([0x00, 0x00, 0x00, 0x01]);
253 assert_eq!(t.to_string(), "fourcc(0x00000001)");
254 }
255
256 #[test]
257 fn display_wave_format() {
258 assert_eq!(
259 CodecTag::wave_format(0x0055).to_string(),
260 "wFormatTag(0x0055)"
261 );
262 }
263
264 #[test]
265 fn display_mp4_oti() {
266 assert_eq!(CodecTag::mp4_object_type(0x40).to_string(), "mp4_oti(0x40)");
267 }
268
269 #[test]
270 fn display_matroska() {
271 assert_eq!(
272 CodecTag::matroska("V_MPEG4/ISO/AVC").to_string(),
273 "matroska(V_MPEG4/ISO/AVC)",
274 );
275 }
276
277 #[test]
278 fn null_resolver_resolves_nothing() {
279 let r = NullCodecResolver;
280 assert!(r.resolve_tag(&CodecTag::fourcc(b"XVID"), None).is_none());
281 assert!(r
282 .resolve_tag(&CodecTag::fourcc(b"XVID"), Some(b"anything"))
283 .is_none());
284 assert!(r
285 .resolve_tag(&CodecTag::wave_format(0x0055), None)
286 .is_none());
287 }
288}