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.
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}