ac_ffmpeg/
lib.rs

1//! Safe Rust interface for FFmpeg libraries. See the `examples` folder for
2//! code examples.
3
4pub mod codec;
5pub mod format;
6pub mod math;
7pub mod packet;
8pub mod time;
9
10use std::{
11    ffi::CStr,
12    fmt::{self, Display, Formatter},
13    io,
14    os::raw::{c_char, c_int},
15    sync::RwLock,
16};
17
18use lazy_static::lazy_static;
19
20lazy_static! {
21    /// Log callback.
22    static ref LOG_CALLBACK: RwLock<LogCallback> = {
23        RwLock::new(LogCallback::new())
24    };
25}
26
27extern "C" {
28    fn ffw_set_log_callback(callback: extern "C" fn(c_int, *const c_char));
29
30    static ffw_error_again: c_int;
31    static ffw_error_eof: c_int;
32    static ffw_error_would_block: c_int;
33    static ffw_error_unknown: c_int;
34
35    fn ffw_error_from_posix(error: c_int) -> c_int;
36    fn ffw_error_to_posix(error: c_int) -> c_int;
37    fn ffw_error_get_error_string(error: c_int, buffer: *mut c_char, buffer_size: usize);
38}
39
40/// A C function passed to the native library as a log callback. The function
41/// calls a closure saved in LOG_CALLBACK (if any).
42extern "C" fn log_callback(level: c_int, message: *const c_char) {
43    let msg = unsafe { CStr::from_ptr(message as _) };
44
45    // level 32 and lower is INFO, WARNING or higher in terms of FFmpeg
46    if level <= 32 {
47        LOG_CALLBACK
48            .read()
49            .unwrap()
50            .call(level as _, &msg.to_string_lossy());
51    }
52}
53
54/// Wrapper around a log closure.
55#[allow(clippy::type_complexity)]
56struct LogCallback {
57    callback: Option<Box<dyn Fn(i32, &str) + Send + Sync>>,
58}
59
60impl LogCallback {
61    /// Create a new empty log callback.
62    fn new() -> LogCallback {
63        LogCallback { callback: None }
64    }
65
66    /// Store a log callback closure.
67    fn set<F>(&mut self, callback: F)
68    where
69        F: 'static + Fn(i32, &str) + Send + Sync,
70    {
71        self.callback = Some(Box::new(callback));
72    }
73
74    /// Call the stored closure (if any).
75    fn call(&self, level: i32, message: &str) {
76        if let Some(callback) = self.callback.as_ref() {
77            callback(level, message);
78        }
79    }
80}
81
82/// Set log callback for FFmpeg. All log messages from FFmpeg will be passed
83/// to a given closure.
84pub fn set_log_callback<F>(callback: F)
85where
86    F: 'static + Fn(i32, &str) + Send + Sync,
87{
88    LOG_CALLBACK.write().unwrap().set(callback);
89
90    unsafe {
91        ffw_set_log_callback(log_callback);
92    }
93}
94
95/// Error variants.
96#[derive(Debug, Clone)]
97enum ErrorVariant {
98    FFmpeg(c_int),
99    Other(String),
100}
101
102/// An error.
103#[derive(Debug, Clone)]
104pub struct Error {
105    variant: ErrorVariant,
106}
107
108impl Error {
109    /// Create a new FFmpeg error.
110    pub fn new<T>(msg: T) -> Self
111    where
112        T: ToString,
113    {
114        Self {
115            variant: ErrorVariant::Other(msg.to_string()),
116        }
117    }
118
119    /// Convert this error into a standard IO error (if possible).
120    pub fn to_io_error(&self) -> Option<io::Error> {
121        if let ErrorVariant::FFmpeg(code) = &self.variant {
122            let posix = unsafe { ffw_error_to_posix(*code) };
123            let err = io::Error::from_raw_os_error(posix as _);
124
125            Some(err)
126        } else {
127            None
128        }
129    }
130
131    /// Create a new FFmpeg error from a given FFmpeg error code.
132    fn from_raw_error_code(code: c_int) -> Self {
133        Self {
134            variant: ErrorVariant::FFmpeg(code),
135        }
136    }
137}
138
139impl Display for Error {
140    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
141        match &self.variant {
142            ErrorVariant::FFmpeg(code) => {
143                let mut buffer = [0u8; 256];
144
145                let buffer_ptr = buffer.as_mut_ptr();
146                let buffer_len = buffer.len();
147
148                let msg = unsafe {
149                    ffw_error_get_error_string(*code, buffer_ptr as _, buffer_len as _);
150
151                    CStr::from_ptr(buffer.as_ptr() as _)
152                        .to_str()
153                        .expect("UTF-8 encoded error string expected")
154                };
155
156                write!(f, "{}", msg)
157            }
158            ErrorVariant::Other(msg) => write!(f, "{}", msg),
159        }
160    }
161}
162
163impl std::error::Error for Error {}