mediadecode_ffmpeg/
channel_layout.rs1use core::{ffi::c_char, slice};
15
16use ffmpeg_next::{ChannelLayout, ffi};
17use mediadecode::channel::{
18 AudioChannelLayout, AudioChannelOrderKind, AudioChannelSpec, ChannelLayoutKind,
19};
20use smol_str::SmolStr;
21use std::vec::Vec;
22
23pub fn channel_layout_kind_from_ffmpeg(value: &ChannelLayout) -> ChannelLayoutKind {
29 match () {
30 () if value.eq(&ChannelLayout::MONO) => ChannelLayoutKind::Mono,
31 () if value.eq(&ChannelLayout::STEREO) => ChannelLayoutKind::Stereo,
32 () if value.eq(&ChannelLayout::STEREO_DOWNMIX) => ChannelLayoutKind::StereoDownmix,
33 () if value.eq(&ChannelLayout::SURROUND) => ChannelLayoutKind::Surround,
34 () if value.eq(&ChannelLayout::QUAD) => ChannelLayoutKind::Quad,
35 () if value.eq(&ChannelLayout::HEXAGONAL) => ChannelLayoutKind::Hexagonal,
36 () if value.eq(&ChannelLayout::OCTAGONAL) => ChannelLayoutKind::Octagonal,
37 () if value.eq(&ChannelLayout::HEXADECAGONAL) => ChannelLayoutKind::Hexadecagonal,
38 () if value.eq(&ChannelLayout::CUBE) => ChannelLayoutKind::Cube,
39 () if value.eq(&ChannelLayout::_2POINT1) => ChannelLayoutKind::Ch2_1,
40 () if value.eq(&ChannelLayout::_2_1) => ChannelLayoutKind::Ch2_1Alt,
41 () if value.eq(&ChannelLayout::_2_2) => ChannelLayoutKind::Ch2_2,
42 () if value.eq(&ChannelLayout::_3POINT1) => ChannelLayoutKind::Ch3_1,
43 () if value.eq(&ChannelLayout::_3POINT1POINT2) => ChannelLayoutKind::Ch3_1_2,
44 () if value.eq(&ChannelLayout::_4POINT0) => ChannelLayoutKind::Ch4_0,
45 () if value.eq(&ChannelLayout::_4POINT1) => ChannelLayoutKind::Ch4_1,
46 () if value.eq(&ChannelLayout::_5POINT0) => ChannelLayoutKind::Ch5_0,
47 () if value.eq(&ChannelLayout::_5POINT0_BACK) => ChannelLayoutKind::Ch5_0Back,
48 () if value.eq(&ChannelLayout::_5POINT1) => ChannelLayoutKind::Ch5_1,
49 () if value.eq(&ChannelLayout::_5POINT1_BACK) => ChannelLayoutKind::Ch5_1Back,
50 () if value.eq(&ChannelLayout::_5POINT1POINT2_BACK) => ChannelLayoutKind::Ch5_1_2Back,
51 () if value.eq(&ChannelLayout::_5POINT1POINT4_BACK) => ChannelLayoutKind::Ch5_1_4Back,
52 () if value.eq(&ChannelLayout::_6POINT0) => ChannelLayoutKind::Ch6_0,
53 () if value.eq(&ChannelLayout::_6POINT0_FRONT) => ChannelLayoutKind::Ch6_0Front,
54 () if value.eq(&ChannelLayout::_6POINT1) => ChannelLayoutKind::Ch6_1,
55 () if value.eq(&ChannelLayout::_6POINT1_BACK) => ChannelLayoutKind::Ch6_1Back,
56 () if value.eq(&ChannelLayout::_6POINT1_FRONT) => ChannelLayoutKind::Ch6_1Front,
57 () if value.eq(&ChannelLayout::_7POINT0) => ChannelLayoutKind::Ch7_0,
58 () if value.eq(&ChannelLayout::_7POINT0_FRONT) => ChannelLayoutKind::Ch7_0Front,
59 () if value.eq(&ChannelLayout::_7POINT1) => ChannelLayoutKind::Ch7_1,
60 () if value.eq(&ChannelLayout::_7POINT1_WIDE) => ChannelLayoutKind::Ch7_1Wide,
61 () if value.eq(&ChannelLayout::_7POINT1_WIDE_BACK) => ChannelLayoutKind::Ch7_1WideBack,
62 () if value.eq(&ChannelLayout::_7POINT1_TOP_BACK) => ChannelLayoutKind::Ch7_1TopBack,
63 () if value.eq(&ChannelLayout::_7POINT1POINT2) => ChannelLayoutKind::Ch7_1_2,
64 () if value.eq(&ChannelLayout::_7POINT1POINT4_BACK) => ChannelLayoutKind::Ch7_1_4Back,
65 () if value.eq(&ChannelLayout::_7POINT2POINT3) => ChannelLayoutKind::Ch7_2_3,
66 () if value.eq(&ChannelLayout::_9POINT1POINT4_BACK) => ChannelLayoutKind::Ch9_1_4Back,
67 () if value.eq(&ChannelLayout::_22POINT2) => ChannelLayoutKind::Ch22_2,
68 () => ChannelLayoutKind::Unknown,
69 }
70}
71
72pub fn audio_channel_order_kind_from_ffmpeg(value: ffi::AVChannelOrder) -> AudioChannelOrderKind {
75 audio_channel_order_kind_from_raw(value as i32)
82}
83
84pub fn audio_channel_order_kind_from_raw(raw: i32) -> AudioChannelOrderKind {
89 match raw {
90 x if x == ffi::AVChannelOrder::AV_CHANNEL_ORDER_NATIVE as i32 => AudioChannelOrderKind::Native,
91 x if x == ffi::AVChannelOrder::AV_CHANNEL_ORDER_CUSTOM as i32 => AudioChannelOrderKind::Custom,
92 x if x == ffi::AVChannelOrder::AV_CHANNEL_ORDER_AMBISONIC as i32 => {
93 AudioChannelOrderKind::Ambisonic
94 }
95 _ => AudioChannelOrderKind::Unspecified,
96 }
97}
98
99pub fn audio_channel_layout_from_ffmpeg(value: &ChannelLayout) -> AudioChannelLayout {
110 unsafe { audio_channel_layout_from_raw_ptr(&value.0 as *const ffi::AVChannelLayout) }
116}
117
118pub unsafe fn audio_channel_layout_from_raw_ptr(
131 ptr: *const ffi::AVChannelLayout,
132) -> AudioChannelLayout {
133 use core::ptr::{addr_of, read_unaligned};
134 let order_raw = unsafe { read_unaligned(addr_of!((*ptr).order) as *const i32) };
140 let order = audio_channel_order_kind_from_raw(order_raw);
141 let nb_channels = unsafe { (*ptr).nb_channels };
142
143 let native_mask = match order {
147 AudioChannelOrderKind::Native | AudioChannelOrderKind::Ambisonic => {
148 let mask = unsafe { (*ptr).u.mask };
150 if mask != 0 { Some(mask) } else { None }
151 }
152 _ => None,
153 };
154
155 let known_kind = if matches!(order, AudioChannelOrderKind::Unspecified) {
166 ChannelLayoutKind::Unknown
167 } else {
168 let layout_ref = unsafe { &*(ptr as *const ChannelLayout) };
172 channel_layout_kind_from_ffmpeg(layout_ref)
173 };
174 let custom_channels_vec = unsafe { custom_channels_raw(ptr, order) };
175 let description = if matches!(order, AudioChannelOrderKind::Unspecified) {
176 SmolStr::default()
177 } else {
178 let layout_ref = unsafe { &*(ptr as *const ChannelLayout) };
180 describe_layout(layout_ref)
181 };
182
183 AudioChannelLayout::new(nb_channels.max(0) as u32)
184 .with_order(order)
185 .with_known_kind(known_kind)
186 .with_native_mask(native_mask)
187 .with_custom_channels(custom_channels_vec)
188 .with_description(description)
189}
190
191unsafe fn custom_channels_raw(
200 ptr: *const ffi::AVChannelLayout,
201 order: AudioChannelOrderKind,
202) -> Vec<AudioChannelSpec> {
203 use core::ptr::{addr_of, read_unaligned};
204 if !matches!(order, AudioChannelOrderKind::Custom) {
205 return Vec::new();
206 }
207 let count = unsafe { (*ptr).nb_channels }.max(0) as usize;
208 if count == 0 {
209 return Vec::new();
210 }
211 let map_ptr = unsafe { (*ptr).u.map };
215 if map_ptr.is_null() {
216 return Vec::new();
217 }
218 let mut out = Vec::with_capacity(count);
225 for index in 0..count {
226 let entry_ptr: *const ffi::AVChannelCustom = unsafe { map_ptr.add(index) };
230 let raw_id = unsafe { read_unaligned(addr_of!((*entry_ptr).id) as *const i32) };
233 let label = unsafe { custom_channel_label_raw(entry_ptr) };
234 out.push(AudioChannelSpec::new(index as u32, raw_id as u32).with_label(label));
235 }
236 out
237}
238
239unsafe fn custom_channel_label_raw(entry_ptr: *const ffi::AVChannelCustom) -> SmolStr {
245 use core::ptr::addr_of;
246 let name_ptr = unsafe { addr_of!((*entry_ptr).name) } as *const u8;
251 let bytes = unsafe { slice::from_raw_parts(name_ptr, 16) };
253 let end = bytes
254 .iter()
255 .position(|byte| *byte == 0)
256 .unwrap_or(bytes.len());
257 if end == 0 {
258 return SmolStr::default();
259 }
260 SmolStr::new(std::string::String::from_utf8_lossy(&bytes[..end]))
261}
262
263#[allow(dead_code)]
264fn custom_channels(layout: &ChannelLayout) -> Vec<AudioChannelSpec> {
265 use core::ptr::{addr_of, read_unaligned};
269 let order_raw = unsafe { read_unaligned(addr_of!(layout.0.order) as *const i32) };
272 if order_raw != ffi::AVChannelOrder::AV_CHANNEL_ORDER_CUSTOM as i32 {
273 return Vec::new();
274 }
275 let count = layout.0.nb_channels.max(0) as usize;
276 if count == 0 {
277 return Vec::new();
278 }
279 let ptr = unsafe { layout.0.u.map };
283 if ptr.is_null() {
284 return Vec::new();
285 }
286 let slice_ref = unsafe { slice::from_raw_parts(ptr, count) };
289 slice_ref
290 .iter()
291 .enumerate()
292 .map(|(index, channel)| {
293 let raw_id = unsafe { read_unaligned(addr_of!(channel.id) as *const i32) };
298 AudioChannelSpec::new(index as u32, raw_id as u32).with_label(custom_channel_label(channel))
299 })
300 .collect()
301}
302
303fn custom_channel_label(channel: &ffi::AVChannelCustom) -> SmolStr {
304 let bytes =
307 unsafe { slice::from_raw_parts(channel.name.as_ptr() as *const u8, channel.name.len()) };
308 let end = bytes
309 .iter()
310 .position(|byte| *byte == 0)
311 .unwrap_or(bytes.len());
312 if end == 0 {
313 return SmolStr::default();
314 }
315 SmolStr::new(std::string::String::from_utf8_lossy(&bytes[..end]))
316}
317
318fn describe_layout(layout: &ChannelLayout) -> SmolStr {
319 let mut buf = std::vec![0 as c_char; 128];
325 let mut needed =
326 unsafe { ffi::av_channel_layout_describe(&layout.0 as *const _, buf.as_mut_ptr(), buf.len()) };
327 if needed < 0 {
328 return SmolStr::default();
329 }
330 if needed as usize >= buf.len() {
331 buf.resize(needed as usize + 1, 0 as c_char);
332 needed = unsafe {
333 ffi::av_channel_layout_describe(&layout.0 as *const _, buf.as_mut_ptr(), buf.len())
334 };
335 if needed < 0 {
336 return SmolStr::default();
337 }
338 }
339 let bytes = unsafe { slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len()) };
341 let end = bytes
342 .iter()
343 .position(|byte| *byte == 0)
344 .unwrap_or(needed as usize);
345 if end == 0 {
346 return SmolStr::default();
347 }
348 SmolStr::new(std::string::String::from_utf8_lossy(&bytes[..end]))
349}