ff_sys/utils.rs
1//! FFmpeg initialization and error conversion utilities.
2
3use std::ffi::CStr;
4use std::sync::Once;
5
6/// FFmpeg initialization guard.
7/// Ensures FFmpeg is initialized exactly once.
8static INIT: Once = Once::new();
9
10/// Ensure FFmpeg is initialized.
11///
12/// This function is idempotent and can be called multiple times safely.
13/// It will only perform initialization once.
14pub fn ensure_initialized() {
15 INIT.call_once(|| {
16 // FFmpeg 4.0+ deprecated av_register_all() and it was removed in later versions.
17 // Modern FFmpeg automatically registers codecs/formats at startup.
18 // This function is kept for potential future initialization needs
19 // (e.g., logging configuration, thread settings).
20 });
21}
22
23/// Convert an FFmpeg error code to a human-readable string.
24///
25/// # Arguments
26///
27/// * `errnum` - The FFmpeg error code (negative value)
28///
29/// # Returns
30///
31/// A string describing the error.
32///
33/// # Safety
34///
35/// This function calls FFmpeg's `av_strerror` which is thread-safe.
36pub fn av_error_string(errnum: i32) -> String {
37 const BUF_SIZE: usize = 256;
38 let mut buf = [0i8; BUF_SIZE];
39
40 // SAFETY: av_strerror writes to the buffer and is thread-safe
41 unsafe {
42 crate::av_strerror(errnum, buf.as_mut_ptr(), BUF_SIZE);
43 }
44
45 // SAFETY: av_strerror null-terminates the buffer
46 let c_str = unsafe { CStr::from_ptr(buf.as_ptr()) };
47 c_str.to_string_lossy().into_owned()
48}
49
50/// Macro to check FFmpeg return values and convert to `Result`.
51///
52/// # Example
53///
54/// ```ignore
55/// let result = check_av_error!(avformat_open_input(...));
56/// ```
57#[macro_export]
58macro_rules! check_av_error {
59 ($expr:expr) => {{
60 let ret = $expr;
61 if ret < 0 {
62 Err($crate::av_error_string(ret))
63 } else {
64 Ok(ret)
65 }
66 }};
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use crate::error_codes;
73
74 #[test]
75 fn ensure_initialized_should_be_idempotent() {
76 // Should not panic when called multiple times
77 ensure_initialized();
78 ensure_initialized();
79 ensure_initialized();
80 }
81
82 #[test]
83 fn av_error_string_should_return_non_empty_message() {
84 let error_str = av_error_string(error_codes::ENOMEM);
85 assert!(!error_str.is_empty());
86 }
87}