pub mod codec;
pub mod format;
pub mod packet;
pub mod time;
use std::{
ffi::CStr,
fmt::{self, Display, Formatter},
io,
os::raw::{c_char, c_int},
sync::RwLock,
};
use lazy_static::lazy_static;
lazy_static! {
static ref LOG_CALLBACK: RwLock<LogCallback> = {
RwLock::new(LogCallback::new())
};
}
extern "C" {
fn ffw_set_log_callback(callback: extern "C" fn(c_int, *const c_char));
fn ffw_error_again() -> c_int;
fn ffw_error_eof() -> c_int;
fn ffw_error_would_block() -> c_int;
fn ffw_error_unknown() -> c_int;
fn ffw_error_from_posix(error: c_int) -> c_int;
fn ffw_error_to_posix(error: c_int) -> c_int;
fn ffw_error_get_error_string(error: c_int, buffer: *mut c_char, buffer_size: usize);
}
extern "C" fn log_callback(level: c_int, message: *const c_char) {
let msg = unsafe { CStr::from_ptr(message as _) };
if level <= 32 {
LOG_CALLBACK
.read()
.unwrap()
.call(level as _, &msg.to_string_lossy());
}
}
#[allow(clippy::type_complexity)]
struct LogCallback {
callback: Option<Box<dyn Fn(i32, &str) + Send + Sync>>,
}
impl LogCallback {
fn new() -> LogCallback {
LogCallback { callback: None }
}
fn set<F>(&mut self, callback: F)
where
F: 'static + Fn(i32, &str) + Send + Sync,
{
self.callback = Some(Box::new(callback));
}
fn call(&self, level: i32, message: &str) {
if let Some(callback) = self.callback.as_ref() {
callback(level, message);
}
}
}
pub fn set_log_callback<F>(callback: F)
where
F: 'static + Fn(i32, &str) + Send + Sync,
{
LOG_CALLBACK.write().unwrap().set(callback);
unsafe {
ffw_set_log_callback(log_callback);
}
}
#[derive(Debug, Clone)]
enum ErrorVariant {
FFmpeg(c_int),
Other(String),
}
#[derive(Debug, Clone)]
pub struct Error {
variant: ErrorVariant,
}
impl Error {
pub fn new<T>(msg: T) -> Self
where
T: ToString,
{
Self {
variant: ErrorVariant::Other(msg.to_string()),
}
}
pub fn to_io_error(&self) -> Option<io::Error> {
if let ErrorVariant::FFmpeg(code) = &self.variant {
let posix = unsafe { ffw_error_to_posix(*code) };
let err = io::Error::from_raw_os_error(posix as _);
Some(err)
} else {
None
}
}
fn from_raw_error_code(code: c_int) -> Self {
Self {
variant: ErrorVariant::FFmpeg(code),
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match &self.variant {
ErrorVariant::FFmpeg(code) => {
let mut buffer = [0u8; 256];
let buffer_ptr = buffer.as_mut_ptr();
let buffer_len = buffer.len();
let msg = unsafe {
ffw_error_get_error_string(*code, buffer_ptr as _, buffer_len as _);
CStr::from_ptr(buffer.as_ptr() as _)
.to_str()
.expect("UTF-8 encoded error string expected")
};
write!(f, "{}", msg)
}
ErrorVariant::Other(msg) => write!(f, "{}", msg),
}
}
}
impl std::error::Error for Error {}