Skip to main content

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.
14///
15/// Installs the [`log` bridge](crate::install_log_bridge), so FFmpeg's own
16/// diagnostics reach the `log` facade under the `ffmpeg` target instead of
17/// going to stderr. That is **process-global** FFmpeg state; see
18/// [`install_log_bridge`](crate::install_log_bridge).
19pub fn ensure_initialized() {
20    INIT.call_once(|| {
21        // FFmpeg 4.0+ deprecated av_register_all() and it was removed in later versions.
22        // Modern FFmpeg automatically registers codecs/formats at startup, so routing
23        // its logging is what is left to do here.
24        crate::install_log_bridge();
25    });
26}
27
28/// Convert an FFmpeg error code to a human-readable string.
29///
30/// # Arguments
31///
32/// * `errnum` - The FFmpeg error code (negative value)
33///
34/// # Returns
35///
36/// A string describing the error.
37///
38/// # Safety
39///
40/// This function calls FFmpeg's `av_strerror` which is thread-safe.
41pub fn av_error_string(errnum: i32) -> String {
42    const BUF_SIZE: usize = 256;
43    let mut buf = [0i8; BUF_SIZE];
44
45    // SAFETY: av_strerror writes to the buffer and is thread-safe
46    unsafe {
47        crate::av_strerror(errnum, buf.as_mut_ptr(), BUF_SIZE);
48    }
49
50    // SAFETY: av_strerror null-terminates the buffer
51    let c_str = unsafe { CStr::from_ptr(buf.as_ptr()) };
52    c_str.to_string_lossy().into_owned()
53}
54
55/// Macro to check FFmpeg return values and convert to `Result`.
56///
57/// # Example
58///
59/// ```ignore
60/// let result = check_av_error!(avformat_open_input(...));
61/// ```
62#[macro_export]
63macro_rules! check_av_error {
64    ($expr:expr) => {{
65        let ret = $expr;
66        if ret < 0 {
67            Err($crate::av_error_string(ret))
68        } else {
69            Ok(ret)
70        }
71    }};
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use crate::error_codes;
78
79    #[test]
80    fn ensure_initialized_should_be_idempotent() {
81        // Should not panic when called multiple times
82        ensure_initialized();
83        ensure_initialized();
84        ensure_initialized();
85    }
86
87    #[test]
88    fn av_error_string_should_return_non_empty_message() {
89        let error_str = av_error_string(error_codes::ENOMEM);
90        assert!(!error_str.is_empty());
91    }
92}