ffmpeg_the_third/codec/
descriptor.rs1use 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 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}