ff_sys/lib.rs
1//! # ff-sys
2//!
3//! Low-level FFmpeg FFI bindings for Rust.
4//!
5//! This crate provides raw FFI bindings to FFmpeg libraries, generated by
6//! [bindgen](https://github.com/rust-lang/rust-bindgen). It is intended as a
7//! building block for higher-level safe wrappers.
8//!
9//! ## Safety
10//!
11//! All functions in this crate are unsafe. Higher-level safe wrappers are provided
12//! by other ff-* crates:
13//! - `ff-probe` - Media metadata extraction
14//! - `ff-decode` - Decoding and seeking
15//! - `ff-encode` - Encoding and export
16//! - `ff-filter` - Filters and effects
17
18// Suppress warnings from auto-generated bindgen code
19#![allow(warnings)]
20#![allow(non_upper_case_globals)]
21#![allow(non_camel_case_types)]
22#![allow(non_snake_case)]
23#![allow(dead_code)]
24#![allow(clippy::all)]
25#![allow(clippy::pedantic)]
26#![allow(clippy::useless_transmute)]
27#![allow(clippy::unnecessary_cast)]
28#![allow(clippy::transmute_int_to_bool)]
29
30// On docs.rs (DOCS_RS=1) the build script emits an empty bindings.rs and sets
31// cfg(docsrs). We include the hand-written stubs instead so that all dependent
32// crates compile without any changes of their own.
33#[cfg(not(docsrs))]
34include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
35
36#[cfg(docsrs)]
37include!("docsrs_stubs.rs");
38
39// Wrapper modules — only compiled when the real bindings are present.
40#[cfg(not(docsrs))]
41pub mod avcodec;
42#[cfg(not(docsrs))]
43pub mod avformat;
44#[cfg(not(docsrs))]
45pub mod swresample;
46#[cfg(not(docsrs))]
47pub mod swscale;
48
49use std::ffi::CStr;
50use std::sync::Once;
51
52/// FFmpeg initialization guard.
53/// Ensures FFmpeg is initialized exactly once.
54static INIT: Once = Once::new();
55
56/// Ensure FFmpeg is initialized.
57///
58/// This function is idempotent and can be called multiple times safely.
59/// It will only perform initialization once.
60pub fn ensure_initialized() {
61 INIT.call_once(|| {
62 // FFmpeg 4.0+ deprecated av_register_all() and it was removed in later versions.
63 // Modern FFmpeg automatically registers codecs/formats at startup.
64 // This function is kept for potential future initialization needs
65 // (e.g., logging configuration, thread settings).
66 });
67}
68
69/// Convert an FFmpeg error code to a human-readable string.
70///
71/// # Arguments
72///
73/// * `errnum` - The FFmpeg error code (negative value)
74///
75/// # Returns
76///
77/// A string describing the error.
78///
79/// # Safety
80///
81/// This function calls FFmpeg's `av_strerror` which is thread-safe.
82pub fn av_error_string(errnum: i32) -> String {
83 const BUF_SIZE: usize = 256;
84 let mut buf = [0i8; BUF_SIZE];
85
86 // SAFETY: av_strerror writes to the buffer and is thread-safe
87 unsafe {
88 av_strerror(errnum, buf.as_mut_ptr(), BUF_SIZE);
89 }
90
91 // SAFETY: av_strerror null-terminates the buffer
92 let c_str = unsafe { CStr::from_ptr(buf.as_ptr()) };
93 c_str.to_string_lossy().into_owned()
94}
95
96/// Macro to check FFmpeg return values and convert to Result.
97///
98/// # Example
99///
100/// ```ignore
101/// let result = check_av_error!(avformat_open_input(...));
102/// ```
103#[macro_export]
104macro_rules! check_av_error {
105 ($expr:expr) => {{
106 let ret = $expr;
107 if ret < 0 {
108 Err($crate::av_error_string(ret))
109 } else {
110 Ok(ret)
111 }
112 }};
113}
114
115/// Common FFmpeg error codes for convenience.
116pub mod error_codes {
117 /// Resource temporarily unavailable (try again).
118 ///
119 /// `AVERROR(EAGAIN)` is platform-specific:
120 /// - Linux/Windows (MinGW): `EAGAIN = 11`, so `AVERROR(EAGAIN) = -11`
121 /// - macOS/BSD: `EAGAIN = 35`, so `AVERROR(EAGAIN) = -35`
122 #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
123 pub const EAGAIN: i32 = -35;
124 #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd")))]
125 pub const EAGAIN: i32 = -11;
126
127 /// End of file/stream
128 pub const EOF: i32 = -541_478_725; // AVERROR_EOF
129
130 /// Out of memory
131 pub const ENOMEM: i32 = -12;
132
133 /// Invalid data
134 pub const EINVAL: i32 = -22;
135}
136
137/// Invalid PTS value (no presentation timestamp).
138///
139/// This constant indicates that a frame or packet does not have a valid presentation timestamp.
140pub const AV_NOPTS_VALUE: i64 = i64::MIN;
141
142/// `AV_BUFFERSRC_FLAG_KEEP_REF` normalized to `i32` for cross-platform use.
143///
144/// bindgen generates `AV_BUFFERSRC_FLAG_KEEP_REF` as `u32` on Linux/macOS
145/// (pkg-config / Homebrew) but as `i32` on Windows (VCPKG). Use this constant
146/// instead of the raw bindgen symbol when calling `av_buffersrc_add_frame_flags`.
147///
148/// The cfg flag `ffmpeg_buffersrc_flag_u32` is emitted by the build script on
149/// platforms where the bindgen type is `u32` (Linux, macOS).
150#[cfg(ffmpeg_buffersrc_flag_u32)]
151pub const BUFFERSRC_FLAG_KEEP_REF: i32 = AV_BUFFERSRC_FLAG_KEEP_REF as i32;
152#[cfg(not(ffmpeg_buffersrc_flag_u32))]
153pub const BUFFERSRC_FLAG_KEEP_REF: i32 = AV_BUFFERSRC_FLAG_KEEP_REF;
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn test_ensure_initialized() {
161 // Should not panic when called multiple times
162 ensure_initialized();
163 ensure_initialized();
164 ensure_initialized();
165 }
166
167 #[test]
168 fn test_av_error_string() {
169 // Test with a known error code
170 let error_str = av_error_string(error_codes::ENOMEM);
171 // The exact message depends on the system, but it should not be empty
172 assert!(!error_str.is_empty());
173 }
174
175 #[test]
176 fn test_error_codes() {
177 // Verify error codes are negative
178 assert!(error_codes::EAGAIN < 0);
179 assert!(error_codes::EOF < 0);
180 assert!(error_codes::ENOMEM < 0);
181 assert!(error_codes::EINVAL < 0);
182 }
183}