1pub 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 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
40extern "C" fn log_callback(level: c_int, message: *const c_char) {
43 let msg = unsafe { CStr::from_ptr(message as _) };
44
45 if level <= 32 {
47 LOG_CALLBACK
48 .read()
49 .unwrap()
50 .call(level as _, &msg.to_string_lossy());
51 }
52}
53
54#[allow(clippy::type_complexity)]
56struct LogCallback {
57 callback: Option<Box<dyn Fn(i32, &str) + Send + Sync>>,
58}
59
60impl LogCallback {
61 fn new() -> LogCallback {
63 LogCallback { callback: None }
64 }
65
66 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 fn call(&self, level: i32, message: &str) {
76 if let Some(callback) = self.callback.as_ref() {
77 callback(level, message);
78 }
79 }
80}
81
82pub 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#[derive(Debug, Clone)]
97enum ErrorVariant {
98 FFmpeg(c_int),
99 Other(String),
100}
101
102#[derive(Debug, Clone)]
104pub struct Error {
105 variant: ErrorVariant,
106}
107
108impl Error {
109 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 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 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 {}