ffmpeg_the_third/codec/
descriptor.rs

1use std::ffi::CStr;
2use std::ptr::NonNull;
3use std::str::from_utf8_unchecked;
4
5use crate::ffi::*;
6use crate::media;
7
8use super::profile::ProfileIter;
9use super::{CodecProperties, Id};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct CodecDescriptor {
13    ptr: NonNull<AVCodecDescriptor>,
14}
15
16impl CodecDescriptor {
17    pub unsafe fn from_raw(ptr: *const AVCodecDescriptor) -> Option<Self> {
18        NonNull::new(ptr as *mut _).map(|ptr| Self { ptr })
19    }
20
21    pub fn as_ptr(self) -> *const AVCodecDescriptor {
22        self.ptr.as_ptr()
23    }
24
25    pub fn id(self) -> Id {
26        unsafe { (*self.as_ptr()).id.into() }
27    }
28
29    pub fn kind(self) -> media::Type {
30        unsafe { (*self.as_ptr()).type_.into() }
31    }
32
33    pub fn name(self) -> &'static str {
34        unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) }
35    }
36
37    pub fn description(self) -> Option<&'static str> {
38        unsafe {
39            let long_name = (*self.as_ptr()).long_name;
40            if long_name.is_null() {
41                None
42            } else {
43                Some(from_utf8_unchecked(CStr::from_ptr(long_name).to_bytes()))
44            }
45        }
46    }
47
48    pub fn props(self) -> CodecProperties {
49        unsafe { CodecProperties::from_bits_truncate((*self.as_ptr()).props) }
50    }
51
52    pub fn mime_types(self) -> Option<MimeTypeIter> {
53        unsafe { MimeTypeIter::from_raw((*self.as_ptr()).mime_types) }
54    }
55
56    pub fn profiles(self) -> Option<ProfileIter> {
57        unsafe {
58            if (*self.as_ptr()).profiles.is_null() {
59                None
60            } else {
61                Some(ProfileIter::new(self.id(), (*self.as_ptr()).profiles))
62            }
63        }
64    }
65}
66
67pub struct CodecDescriptorIter {
68    ptr: *const AVCodecDescriptor,
69}
70
71impl CodecDescriptorIter {
72    pub fn new() -> Self {
73        Self {
74            ptr: std::ptr::null(),
75        }
76    }
77}
78
79impl Default for CodecDescriptorIter {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85impl Iterator for CodecDescriptorIter {
86    type Item = CodecDescriptor;
87
88    fn next(&mut self) -> Option<Self::Item> {
89        unsafe {
90            let next = avcodec_descriptor_next(self.ptr);
91            if let Some(desc) = CodecDescriptor::from_raw(next) {
92                self.ptr = next;
93                Some(desc)
94            } else {
95                None
96            }
97        }
98    }
99}
100
101pub struct MimeTypeIter {
102    ptr: NonNull<*const libc::c_char>,
103}
104
105impl MimeTypeIter {
106    pub unsafe fn from_raw(ptr: *const *const libc::c_char) -> Option<Self> {
107        NonNull::new(ptr as *mut _).map(|ptr| Self { ptr })
108    }
109}
110
111impl Iterator for MimeTypeIter {
112    type Item = &'static str;
113
114    fn next(&mut self) -> Option<Self::Item> {
115        unsafe {
116            let next = *self.ptr.as_ptr();
117            if next.is_null() {
118                return None;
119            }
120
121            self.ptr = NonNull::new_unchecked(self.ptr.as_ptr().add(1));
122            Some(from_utf8_unchecked(CStr::from_ptr(next).to_bytes()))
123        }
124    }
125}
126
127#[cfg(test)]
128mod test {
129    use super::*;
130    use crate::decoder::find;
131
132    #[test]
133    fn descriptor() {
134        let targa = find(Id::TARGA).expect("can find targa decoder");
135        let desc = targa.descriptor().expect("targa has descriptor");
136        assert_eq!(desc.id(), Id::TARGA);
137        assert_eq!(desc.kind(), media::Type::Video);
138        assert_eq!(desc.name(), "targa");
139
140        // --enable-small will remove all `long_name`s. So this can either be null/None
141        // or the correct description
142        assert!(matches!(
143            desc.description(),
144            None | Some("Truevision Targa image")
145        ));
146
147        let props = desc.props();
148        assert!(
149            props.contains(CodecProperties::INTRA_ONLY)
150                && props.contains(CodecProperties::LOSSLESS)
151        );
152
153        let mut mime_types = desc.mime_types().expect("has mime types");
154        assert_eq!(mime_types.next(), Some("image/x-targa"));
155        assert_eq!(mime_types.next(), Some("image/x-tga"));
156        assert_eq!(mime_types.next(), None);
157    }
158}