1use rusty_ffmpeg::ffi::*;
2
3use crate::AVCodecID;
4
5#[derive(Clone, Copy, PartialEq, Eq)]
9pub struct DecoderCodec(*const AVCodec);
10
11impl std::fmt::Debug for DecoderCodec {
12 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13 if let Some(codec) = unsafe { self.0.as_ref() } {
15 let name = unsafe { std::ffi::CStr::from_ptr(codec.name) };
17
18 f.debug_struct("DecoderCodec")
19 .field("name", &name)
20 .field("id", &codec.id)
21 .finish()
22 } else {
23 f.debug_struct("DecoderCodec")
24 .field("name", &"null")
25 .field("id", &AVCodecID::None)
26 .finish()
27 }
28 }
29}
30
31impl DecoderCodec {
32 pub const fn empty() -> Self {
34 Self(std::ptr::null())
35 }
36
37 pub const fn is_empty(&self) -> bool {
39 self.0.is_null()
40 }
41
42 pub fn new(codec_id: AVCodecID) -> Option<Self> {
44 let codec = unsafe { avcodec_find_decoder(codec_id.0 as crate::ffi::AVCodecID) };
46 if codec.is_null() { None } else { Some(Self(codec)) }
47 }
48
49 pub fn by_name(name: &str) -> Option<Self> {
51 let c_name = std::ffi::CString::new(name).ok()?;
52
53 let codec = unsafe { avcodec_find_decoder_by_name(c_name.as_ptr()) };
55 if codec.is_null() { None } else { Some(Self(codec)) }
56 }
57
58 pub const fn as_ptr(&self) -> *const AVCodec {
60 self.0
61 }
62
63 pub const unsafe fn from_ptr(ptr: *const AVCodec) -> Self {
68 Self(ptr)
69 }
70}
71
72#[derive(Clone, Copy, PartialEq, Eq)]
76pub struct EncoderCodec(*const AVCodec);
77
78impl std::fmt::Debug for EncoderCodec {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 if let Some(codec) = unsafe { self.0.as_ref() } {
82 let name = unsafe { std::ffi::CStr::from_ptr(codec.name) };
84
85 f.debug_struct("EncoderCodec")
86 .field("name", &name)
87 .field("id", &codec.id)
88 .finish()
89 } else {
90 f.debug_struct("EncoderCodec")
91 .field("name", &"null")
92 .field("id", &AVCodecID::None)
93 .finish()
94 }
95 }
96}
97
98impl EncoderCodec {
99 pub const fn empty() -> Self {
101 Self(std::ptr::null())
102 }
103
104 pub const fn is_empty(&self) -> bool {
106 self.0.is_null()
107 }
108
109 pub fn new(codec_id: AVCodecID) -> Option<Self> {
111 let codec = unsafe { avcodec_find_encoder(codec_id.0 as crate::ffi::AVCodecID) };
113 if codec.is_null() { None } else { Some(Self(codec)) }
114 }
115
116 pub fn by_name(name: &str) -> Option<Self> {
118 let c_name = std::ffi::CString::new(name).ok()?;
119 let codec = unsafe { avcodec_find_encoder_by_name(c_name.as_ptr()) };
121 if codec.is_null() { None } else { Some(Self(codec)) }
122 }
123
124 pub const fn as_ptr(&self) -> *const AVCodec {
126 self.0
127 }
128
129 pub const unsafe fn from_ptr(ptr: *const AVCodec) -> Self {
134 Self(ptr)
135 }
136}
137
138impl From<EncoderCodec> for *const AVCodec {
139 fn from(codec: EncoderCodec) -> Self {
140 codec.0
141 }
142}
143
144impl From<DecoderCodec> for *const AVCodec {
145 fn from(codec: DecoderCodec) -> Self {
146 codec.0
147 }
148}
149
150#[cfg(test)]
151#[cfg_attr(all(test, coverage_nightly), coverage(off))]
152mod tests {
153 use crate::codec::{AVCodecID, DecoderCodec, EncoderCodec};
154 use crate::ffi::{AVCodec, avcodec_find_decoder, avcodec_find_encoder};
155
156 #[test]
157 fn test_decoder_codec_debug_null() {
158 let decoder_codec = DecoderCodec::empty();
159 let debug_output = format!("{decoder_codec:?}");
160
161 insta::assert_snapshot!(debug_output, @r#"DecoderCodec { name: "null", id: AVCodecID::None }"#);
162 }
163
164 #[test]
165 fn test_decoder_codec_debug_non_null() {
166 let decoder_codec = DecoderCodec::new(AVCodecID::H264).expect("H264 codec should be available");
167 let debug_output = format!("{decoder_codec:?}");
168
169 insta::assert_snapshot!(debug_output, @r#"DecoderCodec { name: "h264", id: 27 }"#);
170 }
171
172 #[test]
173 fn test_decoder_codec_new_invalid_codec_id() {
174 let invalid_codec_id = AVCodecID::None;
175 let result = DecoderCodec::new(invalid_codec_id);
176
177 assert!(
178 result.is_none(),
179 "Expected `DecoderCodec::new` to return None for an invalid codec ID"
180 );
181 }
182
183 #[test]
184 fn test_decoder_codec_by_name_valid() {
185 let result = DecoderCodec::by_name("h264");
186
187 assert!(
188 result.is_some(),
189 "Expected `DecoderCodec::by_name` to return Some for a valid codec name"
190 );
191
192 let codec = result.unwrap();
193 assert!(!codec.as_ptr().is_null(), "Expected a non-null codec pointer");
194 }
195
196 #[test]
197 fn test_decoder_codec_by_name_invalid() {
198 let invalid_codec_name = "nonexistent_codec";
199 let result = DecoderCodec::by_name(invalid_codec_name);
200
201 assert!(
202 result.is_none(),
203 "Expected `DecoderCodec::by_name` to return None for an invalid codec name"
204 );
205 }
206
207 #[test]
208 fn test_decoder_codec_from_ptr_valid() {
209 let codec_ptr = unsafe { avcodec_find_decoder(AVCodecID::H264.into()) };
211 assert!(!codec_ptr.is_null(), "Expected a valid codec pointer for H264");
212
213 let decoder_codec = unsafe { DecoderCodec::from_ptr(codec_ptr) };
215 assert_eq!(
216 decoder_codec.as_ptr(),
217 codec_ptr,
218 "Expected the codec pointer in DecoderCodec to match the original pointer"
219 );
220 }
221
222 #[test]
223 fn test_encoder_codec_debug_valid() {
224 let codec_ptr = unsafe { avcodec_find_encoder(AVCodecID::Mpeg4.into()) };
226
227 assert!(!codec_ptr.is_null(), "Expected a valid codec pointer for MPEG4");
228
229 let encoder_codec = EncoderCodec(codec_ptr);
230 insta::assert_debug_snapshot!(encoder_codec, @r#"
231 EncoderCodec {
232 name: "mpeg4",
233 id: 12,
234 }
235 "#);
236 }
237
238 #[test]
239 fn test_encoder_codec_debug_null() {
240 let encoder_codec = EncoderCodec(std::ptr::null());
241 insta::assert_debug_snapshot!(encoder_codec, @r#"
242 EncoderCodec {
243 name: "null",
244 id: AVCodecID::None,
245 }
246 "#);
247 }
248
249 #[test]
250 fn test_encoder_codec_empty() {
251 let encoder_codec = EncoderCodec::empty();
252 assert!(
253 encoder_codec.as_ptr().is_null(),
254 "Expected the encoder codec pointer to be null"
255 );
256
257 insta::assert_debug_snapshot!(encoder_codec, @r#"
258 EncoderCodec {
259 name: "null",
260 id: AVCodecID::None,
261 }
262 "#);
263 }
264
265 #[test]
266 fn test_encoder_codec_new_invalid_codec() {
267 let invalid_codec_id = AVCodecID::None;
268 let result = EncoderCodec::new(invalid_codec_id);
269
270 assert!(result.is_none(), "Expected None for an invalid codec ID");
271 }
272
273 #[test]
274 fn test_encoder_codec_by_name_valid() {
275 let result = EncoderCodec::by_name("mpeg4");
276 assert!(result.is_some(), "Expected a valid encoder codec for the name {}", "mpeg4");
277
278 let encoder_codec = result.unwrap();
279 assert!(!encoder_codec.as_ptr().is_null(), "Expected a non-null encoder codec pointer");
280 }
281
282 #[test]
283 fn test_encoder_codec_by_name_invalid() {
284 let invalid_encoder_name = "invalid_encoder_name";
285 let result = EncoderCodec::by_name(invalid_encoder_name);
286
287 assert!(
288 result.is_none(),
289 "Expected None for an invalid encoder name {invalid_encoder_name}"
290 );
291 }
292
293 #[test]
294 fn test_encoder_codec_into_raw_ptr() {
295 let valid_codec_id = AVCodecID::Aac;
296 let encoder_codec = EncoderCodec::new(valid_codec_id).expect("Expected a valid encoder codec for AAC");
297 let raw_ptr: *const AVCodec = encoder_codec.into();
298
299 assert_eq!(
300 raw_ptr,
301 encoder_codec.as_ptr(),
302 "The raw pointer should match the encoder codec's internal pointer"
303 );
304 }
305
306 #[test]
307 fn test_decoder_codec_into_raw_ptr() {
308 let valid_codec_id = AVCodecID::Aac;
309 let decoder_codec = DecoderCodec::new(valid_codec_id).expect("Expected a valid decoder codec for AAC");
310 let raw_ptr: *const AVCodec = decoder_codec.into();
311
312 assert_eq!(
313 raw_ptr,
314 decoder_codec.as_ptr(),
315 "The raw pointer should match the decoder codec's internal pointer"
316 );
317 }
318
319 #[test]
320 fn test_codec_into_raw_ptr_empty() {
321 let empty_encoder_codec = EncoderCodec::empty();
322 let raw_ptr: *const AVCodec = empty_encoder_codec.into();
323 assert!(raw_ptr.is_null(), "The raw pointer should be null for an empty EncoderCodec");
324
325 let empty_decoder_codec = DecoderCodec::empty();
326 let raw_ptr: *const AVCodec = empty_decoder_codec.into();
327 assert!(raw_ptr.is_null(), "The raw pointer should be null for an empty DecoderCodec");
328 }
329}