ffmpeg_the_third/util/channel_layout/
channel.rs

1use crate::ffi::*;
2
3use std::ffi::CString;
4
5#[cfg(feature = "serialize")]
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
10pub enum Channel {
11    None,
12    FrontLeft,
13    FrontRight,
14    FrontCenter,
15    LowFrequency,
16    BackLeft,
17    BackRight,
18    FrontLeftOfCenter,
19    FrontRightOfCenter,
20    BackCenter,
21    SideLeft,
22    SideRight,
23    TopCenter,
24    TopFrontLeft,
25    TopFrontCenter,
26    TopFrontRight,
27    TopBackLeft,
28    TopBackCenter,
29    TopBackRight,
30    StereoLeft,
31    StereoRight,
32    WideLeft,
33    WideRight,
34    SurroundDirectLeft,
35    SurroundDirectRight,
36    LowFrequency2,
37    TopSideLeft,
38    TopSideRight,
39    BottomFrontCenter,
40    BottomFrontLeft,
41    BottomFrontRight,
42
43    #[cfg(feature = "ffmpeg_7_1")]
44    SideSurroundLeft,
45    #[cfg(feature = "ffmpeg_7_1")]
46    SideSurroundRight,
47    #[cfg(feature = "ffmpeg_7_1")]
48    TopSurroundLeft,
49    #[cfg(feature = "ffmpeg_7_1")]
50    TopSurroundRight,
51
52    #[cfg(feature = "ffmpeg_8_0")]
53    BinauralLeft,
54    #[cfg(feature = "ffmpeg_8_0")]
55    BinauralRight,
56
57    /// Channel is empty and can be safely skipped.
58    Unused,
59
60    /// Channel contains data, but its position is unknown.
61    Unknown,
62
63    /// Defines the start of channel IDs when using Ambisonic.
64    AmbisonicBase,
65    /// Defines the end of channel IDs when using Ambisonic.
66    AmbisonicEnd,
67}
68
69impl Channel {
70    /// Get an abbreviated, human-readable string describing this channel.
71    pub fn name(self) -> String {
72        let mut buf = vec![0u8; 32];
73
74        unsafe {
75            let ret_val = av_channel_name(buf.as_mut_ptr() as _, buf.len(), AVChannel::from(self));
76
77            match usize::try_from(ret_val) {
78                Ok(out_len) if out_len > 0 => {
79                    #[cfg(feature = "ffmpeg_6_1")]
80                    // 6.1 changed out_len to include the NUL byte, which we don't want
81                    let out_len = out_len - 1;
82
83                    buf.truncate(out_len);
84                    String::from_utf8_unchecked(buf)
85                }
86                // `av_channel_name` returned an error, or 0 bytes written.
87                _ => String::new(),
88            }
89        }
90    }
91
92    /// Get a human-readable string describing this channel.
93    pub fn description(self) -> String {
94        let mut buf = vec![0u8; 256];
95
96        unsafe {
97            let ret_val =
98                av_channel_description(buf.as_mut_ptr() as _, buf.len(), AVChannel::from(self));
99
100            match usize::try_from(ret_val) {
101                Ok(out_len) if out_len > 0 => {
102                    #[cfg(feature = "ffmpeg_6_1")]
103                    // 6.1 changed out_len to include the NUL byte, which we don't want
104                    let out_len = out_len - 1;
105
106                    buf.truncate(out_len);
107                    String::from_utf8_unchecked(buf)
108                }
109                // `av_channel_description` returned an error, or 0 bytes written.
110                _ => String::new(),
111            }
112        }
113    }
114
115    /// This is the inverse function of [`name`][Channel::name].
116    pub fn from_string<S: AsRef<str>>(name: S) -> Self {
117        let cstr = CString::new(name.as_ref()).expect("no nul byte in name");
118        Self::from(unsafe { av_channel_from_string(cstr.as_ptr()) })
119    }
120}
121
122impl From<AVChannel> for Channel {
123    fn from(value: AVChannel) -> Self {
124        use crate::ffi::AVChannel::*;
125        use Channel::*;
126
127        match value {
128            AV_CHAN_NONE => None,
129            AV_CHAN_FRONT_LEFT => FrontLeft,
130            AV_CHAN_FRONT_RIGHT => FrontRight,
131            AV_CHAN_FRONT_CENTER => FrontCenter,
132            AV_CHAN_LOW_FREQUENCY => LowFrequency,
133            AV_CHAN_BACK_LEFT => BackLeft,
134            AV_CHAN_BACK_RIGHT => BackRight,
135            AV_CHAN_FRONT_LEFT_OF_CENTER => FrontLeftOfCenter,
136            AV_CHAN_FRONT_RIGHT_OF_CENTER => FrontRightOfCenter,
137            AV_CHAN_BACK_CENTER => BackCenter,
138            AV_CHAN_SIDE_LEFT => SideLeft,
139            AV_CHAN_SIDE_RIGHT => SideRight,
140            AV_CHAN_TOP_CENTER => TopCenter,
141            AV_CHAN_TOP_FRONT_LEFT => TopFrontLeft,
142            AV_CHAN_TOP_FRONT_CENTER => TopFrontCenter,
143            AV_CHAN_TOP_FRONT_RIGHT => TopFrontRight,
144            AV_CHAN_TOP_BACK_LEFT => TopBackLeft,
145            AV_CHAN_TOP_BACK_CENTER => TopBackCenter,
146            AV_CHAN_TOP_BACK_RIGHT => TopBackRight,
147            AV_CHAN_STEREO_LEFT => StereoLeft,
148            AV_CHAN_STEREO_RIGHT => StereoRight,
149            AV_CHAN_WIDE_LEFT => WideLeft,
150            AV_CHAN_WIDE_RIGHT => WideRight,
151            AV_CHAN_SURROUND_DIRECT_LEFT => SurroundDirectLeft,
152            AV_CHAN_SURROUND_DIRECT_RIGHT => SurroundDirectRight,
153            AV_CHAN_LOW_FREQUENCY_2 => LowFrequency2,
154            AV_CHAN_TOP_SIDE_LEFT => TopSideLeft,
155            AV_CHAN_TOP_SIDE_RIGHT => TopSideRight,
156            AV_CHAN_BOTTOM_FRONT_CENTER => BottomFrontCenter,
157            AV_CHAN_BOTTOM_FRONT_LEFT => BottomFrontLeft,
158            AV_CHAN_BOTTOM_FRONT_RIGHT => BottomFrontRight,
159
160            #[cfg(feature = "ffmpeg_7_1")]
161            AV_CHAN_SIDE_SURROUND_LEFT => SideSurroundLeft,
162            #[cfg(feature = "ffmpeg_7_1")]
163            AV_CHAN_SIDE_SURROUND_RIGHT => SideSurroundRight,
164            #[cfg(feature = "ffmpeg_7_1")]
165            AV_CHAN_TOP_SURROUND_LEFT => TopSurroundLeft,
166            #[cfg(feature = "ffmpeg_7_1")]
167            AV_CHAN_TOP_SURROUND_RIGHT => TopSurroundRight,
168
169            #[cfg(feature = "ffmpeg_8_0")]
170            AV_CHAN_BINAURAL_LEFT => BinauralLeft,
171            #[cfg(feature = "ffmpeg_8_0")]
172            AV_CHAN_BINAURAL_RIGHT => BinauralRight,
173
174            AV_CHAN_UNUSED => Unused,
175            AV_CHAN_UNKNOWN => Unknown,
176            AV_CHAN_AMBISONIC_BASE => AmbisonicBase,
177            AV_CHAN_AMBISONIC_END => AmbisonicEnd,
178
179            #[cfg(feature = "non-exhaustive-enums")]
180            _ => unimplemented!(),
181        }
182    }
183}
184
185impl From<Channel> for AVChannel {
186    fn from(value: Channel) -> Self {
187        use crate::ffi::AVChannel::*;
188        use Channel::*;
189
190        match value {
191            None => AV_CHAN_NONE,
192            FrontLeft => AV_CHAN_FRONT_LEFT,
193            FrontRight => AV_CHAN_FRONT_RIGHT,
194            FrontCenter => AV_CHAN_FRONT_CENTER,
195            LowFrequency => AV_CHAN_LOW_FREQUENCY,
196            BackLeft => AV_CHAN_BACK_LEFT,
197            BackRight => AV_CHAN_BACK_RIGHT,
198            FrontLeftOfCenter => AV_CHAN_FRONT_LEFT_OF_CENTER,
199            FrontRightOfCenter => AV_CHAN_FRONT_RIGHT_OF_CENTER,
200            BackCenter => AV_CHAN_BACK_CENTER,
201            SideLeft => AV_CHAN_SIDE_LEFT,
202            SideRight => AV_CHAN_SIDE_RIGHT,
203            TopCenter => AV_CHAN_TOP_CENTER,
204            TopFrontLeft => AV_CHAN_TOP_FRONT_LEFT,
205            TopFrontCenter => AV_CHAN_TOP_FRONT_CENTER,
206            TopFrontRight => AV_CHAN_TOP_FRONT_RIGHT,
207            TopBackLeft => AV_CHAN_TOP_BACK_LEFT,
208            TopBackCenter => AV_CHAN_TOP_BACK_CENTER,
209            TopBackRight => AV_CHAN_TOP_BACK_RIGHT,
210            StereoLeft => AV_CHAN_STEREO_LEFT,
211            StereoRight => AV_CHAN_STEREO_RIGHT,
212            WideLeft => AV_CHAN_WIDE_LEFT,
213            WideRight => AV_CHAN_WIDE_RIGHT,
214            SurroundDirectLeft => AV_CHAN_SURROUND_DIRECT_LEFT,
215            SurroundDirectRight => AV_CHAN_SURROUND_DIRECT_RIGHT,
216            LowFrequency2 => AV_CHAN_LOW_FREQUENCY_2,
217            TopSideLeft => AV_CHAN_TOP_SIDE_LEFT,
218            TopSideRight => AV_CHAN_TOP_SIDE_RIGHT,
219            BottomFrontCenter => AV_CHAN_BOTTOM_FRONT_CENTER,
220            BottomFrontLeft => AV_CHAN_BOTTOM_FRONT_LEFT,
221            BottomFrontRight => AV_CHAN_BOTTOM_FRONT_RIGHT,
222
223            #[cfg(feature = "ffmpeg_7_1")]
224            SideSurroundLeft => AV_CHAN_SIDE_SURROUND_LEFT,
225            #[cfg(feature = "ffmpeg_7_1")]
226            SideSurroundRight => AV_CHAN_SIDE_SURROUND_RIGHT,
227            #[cfg(feature = "ffmpeg_7_1")]
228            TopSurroundLeft => AV_CHAN_TOP_SURROUND_LEFT,
229            #[cfg(feature = "ffmpeg_7_1")]
230            TopSurroundRight => AV_CHAN_TOP_SURROUND_RIGHT,
231
232            #[cfg(feature = "ffmpeg_8_0")]
233            BinauralLeft => AV_CHAN_BINAURAL_LEFT,
234            #[cfg(feature = "ffmpeg_8_0")]
235            BinauralRight => AV_CHAN_BINAURAL_RIGHT,
236
237            Unused => AV_CHAN_UNUSED,
238            Unknown => AV_CHAN_UNKNOWN,
239            AmbisonicBase => AV_CHAN_AMBISONIC_BASE,
240            AmbisonicEnd => AV_CHAN_AMBISONIC_END,
241        }
242    }
243}
244
245#[cfg(test)]
246mod test {
247    use super::*;
248
249    // just test everything
250    const TEST_VALUES: &[Channel] = &[
251        Channel::None,
252        Channel::FrontLeft,
253        Channel::FrontRight,
254        Channel::FrontCenter,
255        Channel::LowFrequency,
256        Channel::BackLeft,
257        Channel::BackRight,
258        Channel::FrontLeftOfCenter,
259        Channel::FrontRightOfCenter,
260        Channel::BackCenter,
261        Channel::SideLeft,
262        Channel::SideRight,
263        Channel::TopCenter,
264        Channel::TopFrontLeft,
265        Channel::TopFrontCenter,
266        Channel::TopFrontRight,
267        Channel::TopBackLeft,
268        Channel::TopBackCenter,
269        Channel::TopBackRight,
270        Channel::StereoLeft,
271        Channel::StereoRight,
272        Channel::WideLeft,
273        Channel::WideRight,
274        Channel::SurroundDirectLeft,
275        Channel::SurroundDirectRight,
276        Channel::LowFrequency2,
277        Channel::TopSideLeft,
278        Channel::TopSideRight,
279        Channel::BottomFrontCenter,
280        Channel::BottomFrontLeft,
281        Channel::BottomFrontRight,
282        Channel::Unused,
283        Channel::Unknown,
284        Channel::AmbisonicBase,
285        Channel::AmbisonicEnd,
286    ];
287
288    #[test]
289    fn name() {
290        for ch in TEST_VALUES {
291            let name = ch.name();
292            assert!(!name.is_empty());
293            println!("{name}");
294        }
295    }
296
297    #[test]
298    fn description() {
299        for ch in TEST_VALUES {
300            let desc = ch.description();
301            assert!(!desc.is_empty());
302            println!("{desc}");
303        }
304    }
305
306    #[test]
307    fn from_string() {
308        for ch in TEST_VALUES {
309            let name = ch.name();
310            let found = Channel::from_string(name);
311            assert_eq!(found, *ch);
312        }
313    }
314}