From fc4479254d095d56413aa61c9ce53c4c25903335 Mon Sep 17 00:00:00 2001 From: Julian Wiesler Date: Sat, 5 Feb 2022 15:18:39 +0100 Subject: [PATCH] Log callback interface --- Cargo.toml | 2 + src/lib.rs | 10 ++++ src/util/log/callback.rs | 111 +++++++++++++++++++++++++++++++++++++++ src/util/log/level.rs | 15 ++++++ src/util/log/mod.rs | 3 ++ 5 files changed, 141 insertions(+) create mode 100644 src/util/log/callback.rs diff --git a/Cargo.toml b/Cargo.toml index 68d6262b..1362e811 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,6 +107,8 @@ rpi = [] [dependencies] libc = "0.2" bitflags = "1.2" +log = "0.4" +vsprintf = "2.0" [dependencies.image] version = "0.23" diff --git a/src/lib.rs b/src/lib.rs index ca137a55..75d85779 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ pub extern crate ffmpeg_sys_next as sys; #[cfg(feature = "image")] extern crate image; extern crate libc; +extern crate log as log_crate; pub use sys as ffi; @@ -99,6 +100,14 @@ fn init_filter() { #[cfg(not(feature = "filter"))] fn init_filter() {} +#[cfg(all(target_arch = "x86_64", any(target_family = "windows", target_family = "unix")))] +fn init_log() { + util::log::callback::set_logging_callback(); +} + +#[cfg(not(all(target_arch = "x86_64", any(target_family = "windows", target_family = "unix"))))] +fn init_log() {} + #[cfg_attr( any(feature = "ffmpeg4", feature = "ffmpeg41", feature = "ffmpeg42"), deprecated( @@ -111,6 +120,7 @@ pub fn init() -> Result<(), Error> { init_format(); init_device(); init_filter(); + init_log(); Ok(()) } diff --git a/src/util/log/callback.rs b/src/util/log/callback.rs new file mode 100644 index 00000000..e5ba6a3d --- /dev/null +++ b/src/util/log/callback.rs @@ -0,0 +1,111 @@ +use std::convert::TryInto; +use std::ffi::CStr; +use std::io::Error; + +use libc::c_char; +use libc::c_int; +use log_crate::LevelFilter; + +use util::log::Level; + +// This is ugly, but va_list is not stabilized + +#[cfg(all(target_arch = "x86_64", target_family = "windows"))] +pub type Args = sys::va_list; + +#[cfg(all(target_arch = "x86_64", target_family = "unix"))] +pub type Args = *mut sys::__va_list_tag; + +pub struct LogContext { + context: usize, + level: Level, + fmt: *const c_char, + args: Args, +} + +impl LogContext { + /// Formats the message + #[inline] + pub fn to_message(&self) -> Result { + unsafe { vsprintf::vsprintf(self.fmt, self.args) } + } + + /// The format string + #[inline] + pub fn format(&self) -> &CStr { + unsafe { CStr::from_ptr(self.fmt) } + } + + /// The log level + #[inline] + pub fn level(&self) -> Level { + self.level + } + + /// The log context. Mostly the address of whatever component called the log function. + #[inline] + pub fn context(&self) -> usize { + self.context + } + + /// The log varargs. + /// + /// **Platform dependant**, use with caution. + #[inline] + pub unsafe fn args(&self) -> Args { + self.args + } +} + +pub trait Callback { + fn call(context: &LogContext); +} + +unsafe extern "C" fn wrapped_callback(context: *mut libc::c_void, level: c_int, fmt: *const c_char, args: Args) { + let context = LogContext { + context: context as usize, + level: level.try_into().unwrap_or(Level::Info), + fmt, + args, + }; + T::call(&context); +} + +/// Sets the log callback +pub fn set_callback() { + unsafe { + sys::av_log_set_callback(Some(wrapped_callback::)); + } +} + +/// Resets the log callback +pub fn reset_callback() { + unsafe { + sys::av_log_set_callback(None); + } +} + +/// Sets the logging callback +/// +/// Logs using the log crate to the target 'ffmpeg' +pub fn set_logging_callback() { + set_callback::(); +} + +/// Logs using the log crate to the target 'ffmpeg' +pub struct LoggingCallback; + +impl Callback for LoggingCallback { + fn call(context: &LogContext) { + if let Some(log_level) = LevelFilter::from(context.level()).to_level() { + // Don't format when level is disabled + if log::log_enabled!(log_level) { + match context.to_message() { + Ok(message) => log::log!(target: "ffmpeg", log_level, "{}", message.trim()), + Err(e) => + log::warn!(target: "ffmpeg", "failed to format ffmpeg log message: {:?}", e), + } + } + } + } +} diff --git a/src/util/log/level.rs b/src/util/log/level.rs index 2c184927..85d26276 100644 --- a/src/util/log/level.rs +++ b/src/util/log/level.rs @@ -3,6 +3,8 @@ use std::convert::TryFrom; use ffi::*; use libc::c_int; +use log_crate::LevelFilter; + #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum Level { Quiet, @@ -52,3 +54,16 @@ impl From for c_int { } } } + +impl From for LevelFilter { + fn from(level: Level) -> LevelFilter { + match level { + Level::Quiet => LevelFilter::Off, + Level::Trace => LevelFilter::Trace, + Level::Debug | Level::Verbose => LevelFilter::Debug, + Level::Info => LevelFilter::Info, + Level::Warning => LevelFilter::Warn, + Level::Error | Level::Fatal | Level::Panic => LevelFilter::Error, + } + } +} diff --git a/src/util/log/mod.rs b/src/util/log/mod.rs index 69bc4c78..70066deb 100644 --- a/src/util/log/mod.rs +++ b/src/util/log/mod.rs @@ -7,6 +7,9 @@ pub use self::flag::Flags; use ffi::*; use std::convert::TryInto; +#[cfg(all(target_arch = "x86_64", any(target_family = "windows", target_family = "unix")))] +pub mod callback; + pub fn set_level(value: Level) { unsafe { av_log_set_level(value.into()) } }