use std::fmt::Debug;
use abi_stable::{
declare_root_module_statics,
library::RootModule,
package_version_strings, sabi_trait,
sabi_types::VersionStrings,
std_types::{RBox, RResult, RString, RVec},
StableAbi,
};
use log::{LevelFilter, SetLoggerError};
use once_cell::sync::OnceCell;
use types::PhaneronPlugin;
pub use crate::{
audio::{AudioChannelLayout, AudioFormat},
colour::*,
graph::{AudioInputId, AudioOutputId, VideoInputId, VideoOutputId},
video::{InterlaceMode, VideoFormat},
};
mod audio;
mod colour;
mod graph;
mod video;
pub mod traits;
pub mod types;
static LOGGER: OnceCell<PluginLogger> = OnceCell::new();
#[repr(C)]
#[derive(StableAbi)]
pub struct PhaneronPluginContext {
logging_context: PhaneronLoggingContext_TO<'static, RBox<()>>,
}
impl PhaneronPluginContext {
pub fn new(logging_context: PhaneronLoggingContext_TO<'static, RBox<()>>) -> Self {
PhaneronPluginContext { logging_context }
}
}
impl Debug for PhaneronPluginContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PhaneronPluginContext").finish()
}
}
#[repr(usize)]
#[derive(StableAbi)]
pub enum LogLevel {
Error = 1,
Warn,
Info,
Debug,
Trace,
}
impl From<log::Level> for LogLevel {
fn from(value: log::Level) -> Self {
match value {
log::Level::Error => LogLevel::Error,
log::Level::Warn => LogLevel::Warn,
log::Level::Info => LogLevel::Info,
log::Level::Debug => LogLevel::Debug,
log::Level::Trace => LogLevel::Trace,
}
}
}
#[sabi_trait]
pub trait PhaneronLoggingContext: Send + Sync + Clone {
fn log(&self, level: LogLevel, message: RString);
}
#[repr(C)]
#[derive(StableAbi)]
#[sabi(kind(Prefix(prefix_ref = PhaneronPluginRootModuleRef)))]
#[sabi(missing_field(panic))]
pub struct PhaneronPluginRootModule {
#[sabi(last_prefix_field)]
pub load:
extern "C" fn(load_context: PhaneronPluginContext) -> RResult<PhaneronPlugin, RString>,
}
impl RootModule for PhaneronPluginRootModuleRef {
declare_root_module_statics! {PhaneronPluginRootModuleRef}
const BASE_NAME: &'static str = "phaneron-plugin";
const NAME: &'static str = "phaneron-plugin";
const VERSION_STRINGS: VersionStrings = package_version_strings!();
}
#[repr(C)]
#[derive(Clone, StableAbi)]
pub struct VideoFrameWithId {
pub output_id: VideoOutputId,
pub frame: types::VideoFrame,
}
impl VideoFrameWithId {
pub fn new(output_id: VideoOutputId, frame: types::VideoFrame) -> Self {
Self { output_id, frame }
}
}
#[repr(C)]
#[derive(Clone, StableAbi)]
pub struct AudioFrameWithId {
pub output_id: AudioOutputId,
pub frame: types::AudioFrame,
}
impl AudioFrameWithId {
pub fn new(output_id: AudioOutputId, frame: types::AudioFrame) -> Self {
Self { output_id, frame }
}
}
#[repr(C)]
#[derive(Default, StableAbi)]
pub struct ShaderParams {
params: RVec<ShaderParam>,
}
impl ShaderParams {
pub fn set_param_video_frame_input(&mut self, video_frame: types::VideoFrame) {
self.params.push(ShaderParam::VideoFrameInput(video_frame));
}
pub fn set_param_u32_input(&mut self, val: u32) {
self.params.push(ShaderParam::U32Input(val));
}
pub fn set_param_f32_input(&mut self, val: f32) {
self.params.push(ShaderParam::F32Input(val));
}
pub fn set_param_video_frame_output(&mut self, width: usize, height: usize) {
self.params
.push(ShaderParam::VideoFrameOutput { width, height });
}
pub fn get_params(&self) -> &RVec<ShaderParam> {
&self.params
}
}
#[repr(C)]
#[derive(StableAbi)]
pub enum ShaderParam {
VideoFrameInput(types::VideoFrame),
U32Input(u32),
F32Input(f32),
VideoFrameOutput { width: usize, height: usize },
}
pub struct PluginLogger {
context: PhaneronLoggingContext_TO<'static, RBox<()>>,
}
impl PluginLogger {
pub fn init(&'static self) -> Result<(), SetLoggerError> {
log::set_logger(self)?;
log::set_max_level(LevelFilter::Trace);
Ok(())
}
}
impl Debug for PluginLogger {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PluginLogger").finish()
}
}
impl log::Log for PluginLogger {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) {
self.context
.log(record.level().into(), record.args().to_string().into())
}
}
fn flush(&self) {}
}
pub fn get_logger(context: &PhaneronPluginContext) -> &'static PluginLogger {
let logger = PluginLogger {
context: context.logging_context.clone(),
};
LOGGER.set(logger).unwrap();
LOGGER.get().unwrap()
}