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}