mediadecode_ffmpeg/
sample_format.rs1use core::fmt;
16
17use ffmpeg_next::{
18 ffi::AVSampleFormat,
19 format::{Sample, sample::Type as SampleType},
20};
21
22#[repr(transparent)]
24#[derive(Copy, Clone, Eq, PartialEq, Hash)]
25pub struct SampleFormat(i32);
26
27impl SampleFormat {
28 #[inline]
31 pub const fn from_raw(raw: i32) -> Self {
32 Self(raw)
33 }
34
35 #[inline]
37 pub const fn raw(self) -> i32 {
38 self.0
39 }
40
41 #[inline]
43 pub const fn is_planar(self) -> bool {
44 matches!(
45 self,
46 Self::U8P | Self::S16P | Self::S32P | Self::S64P | Self::FLTP | Self::DBLP,
47 )
48 }
49
50 #[inline]
52 pub const fn is_packed(self) -> bool {
53 matches!(
54 self,
55 Self::U8 | Self::S16 | Self::S32 | Self::S64 | Self::FLT | Self::DBL,
56 )
57 }
58
59 #[inline]
62 pub const fn bytes_per_sample(self) -> Option<u32> {
63 let bytes = match self {
64 Self::U8 | Self::U8P => 1,
65 Self::S16 | Self::S16P => 2,
66 Self::S32 | Self::S32P | Self::FLT | Self::FLTP => 4,
67 Self::S64 | Self::S64P | Self::DBL | Self::DBLP => 8,
68 _ => return None,
69 };
70 Some(bytes)
71 }
72
73 pub const NONE: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_NONE as i32);
77
78 pub const U8: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_U8 as i32);
82 pub const S16: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_S16 as i32);
84 pub const S32: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_S32 as i32);
86 pub const S64: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_S64 as i32);
88 pub const FLT: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_FLT as i32);
90 pub const DBL: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_DBL as i32);
92
93 pub const U8P: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_U8P as i32);
97 pub const S16P: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_S16P as i32);
99 pub const S32P: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_S32P as i32);
101 pub const S64P: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_S64P as i32);
103 pub const FLTP: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_FLTP as i32);
105 pub const DBLP: Self = Self(AVSampleFormat::AV_SAMPLE_FMT_DBLP as i32);
107
108 #[inline]
120 pub const fn to_ffmpeg(self) -> Option<Sample> {
121 Some(match self {
122 Self::U8 => Sample::U8(SampleType::Packed),
123 Self::S16 => Sample::I16(SampleType::Packed),
124 Self::S32 => Sample::I32(SampleType::Packed),
125 Self::S64 => Sample::I64(SampleType::Packed),
126 Self::FLT => Sample::F32(SampleType::Packed),
127 Self::DBL => Sample::F64(SampleType::Packed),
128 Self::U8P => Sample::U8(SampleType::Planar),
129 Self::S16P => Sample::I16(SampleType::Planar),
130 Self::S32P => Sample::I32(SampleType::Planar),
131 Self::S64P => Sample::I64(SampleType::Planar),
132 Self::FLTP => Sample::F32(SampleType::Planar),
133 Self::DBLP => Sample::F64(SampleType::Planar),
134 _ => return None,
135 })
136 }
137
138 #[inline]
143 pub fn from_ffmpeg(value: Sample) -> Self {
144 let raw: AVSampleFormat = value.into();
145 Self::from_raw(raw as i32)
146 }
147}
148
149impl fmt::Debug for SampleFormat {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 let name = match *self {
152 Self::NONE => "NONE",
153 Self::U8 => "U8",
154 Self::S16 => "S16",
155 Self::S32 => "S32",
156 Self::S64 => "S64",
157 Self::FLT => "FLT",
158 Self::DBL => "DBL",
159 Self::U8P => "U8P",
160 Self::S16P => "S16P",
161 Self::S32P => "S32P",
162 Self::S64P => "S64P",
163 Self::FLTP => "FLTP",
164 Self::DBLP => "DBLP",
165 _ => return write!(f, "SampleFormat({})", self.0),
166 };
167 write!(f, "SampleFormat::{name}")
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn known_constants_match_av_values() {
177 assert_eq!(
178 SampleFormat::S16.raw(),
179 AVSampleFormat::AV_SAMPLE_FMT_S16 as i32
180 );
181 assert_eq!(
182 SampleFormat::FLTP.raw(),
183 AVSampleFormat::AV_SAMPLE_FMT_FLTP as i32
184 );
185 assert_eq!(SampleFormat::NONE.raw(), -1);
186 }
187
188 #[test]
189 fn planar_packed_partition_is_complete() {
190 let all_packed = [
191 SampleFormat::U8,
192 SampleFormat::S16,
193 SampleFormat::S32,
194 SampleFormat::S64,
195 SampleFormat::FLT,
196 SampleFormat::DBL,
197 ];
198 let all_planar = [
199 SampleFormat::U8P,
200 SampleFormat::S16P,
201 SampleFormat::S32P,
202 SampleFormat::S64P,
203 SampleFormat::FLTP,
204 SampleFormat::DBLP,
205 ];
206 for f in all_packed {
207 assert!(f.is_packed());
208 assert!(!f.is_planar());
209 }
210 for f in all_planar {
211 assert!(f.is_planar());
212 assert!(!f.is_packed());
213 }
214 }
215
216 #[test]
217 fn bytes_per_sample_matches_width() {
218 assert_eq!(SampleFormat::U8.bytes_per_sample(), Some(1));
219 assert_eq!(SampleFormat::S16.bytes_per_sample(), Some(2));
220 assert_eq!(SampleFormat::S32P.bytes_per_sample(), Some(4));
221 assert_eq!(SampleFormat::FLTP.bytes_per_sample(), Some(4));
222 assert_eq!(SampleFormat::DBL.bytes_per_sample(), Some(8));
223 assert_eq!(SampleFormat::NONE.bytes_per_sample(), None);
224 assert_eq!(SampleFormat::from_raw(99_999).bytes_per_sample(), None);
225 }
226
227 #[test]
228 fn debug_uses_name_for_known_formats() {
229 assert_eq!(format!("{:?}", SampleFormat::S16), "SampleFormat::S16");
230 assert_eq!(format!("{:?}", SampleFormat::FLTP), "SampleFormat::FLTP");
231 }
232
233 #[test]
234 fn debug_falls_back_to_raw_for_unknown() {
235 assert_eq!(
236 format!("{:?}", SampleFormat::from_raw(99_999)),
237 "SampleFormat(99999)"
238 );
239 }
240}