1use std::fmt::Debug;
64
65use abi_stable::{
66 declare_root_module_statics,
67 library::RootModule,
68 package_version_strings, sabi_trait,
69 sabi_types::VersionStrings,
70 std_types::{RBox, RResult, RString, RVec},
71 StableAbi,
72};
73use log::{LevelFilter, SetLoggerError};
74use once_cell::sync::OnceCell;
75use types::PhaneronPlugin;
76
77pub use crate::{
78 audio::{AudioChannelLayout, AudioFormat},
79 colour::*,
80 graph::{AudioInputId, AudioOutputId, VideoInputId, VideoOutputId},
81 video::{InterlaceMode, VideoFormat},
82};
83
84mod audio;
85mod colour;
86mod graph;
87mod video;
88
89pub mod traits;
90pub mod types;
91
92static LOGGER: OnceCell<PluginLogger> = OnceCell::new();
95
96#[repr(C)]
98#[derive(StableAbi)]
99pub struct PhaneronPluginContext {
100 logging_context: PhaneronLoggingContext_TO<'static, RBox<()>>,
101}
102
103impl PhaneronPluginContext {
104 pub fn new(logging_context: PhaneronLoggingContext_TO<'static, RBox<()>>) -> Self {
105 PhaneronPluginContext { logging_context }
106 }
107}
108
109impl Debug for PhaneronPluginContext {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 f.debug_struct("PhaneronPluginContext").finish()
112 }
113}
114
115#[repr(usize)]
118#[derive(StableAbi)]
119pub enum LogLevel {
120 Error = 1,
121 Warn,
122 Info,
123 Debug,
124 Trace,
125}
126
127impl From<log::Level> for LogLevel {
128 fn from(value: log::Level) -> Self {
129 match value {
130 log::Level::Error => LogLevel::Error,
131 log::Level::Warn => LogLevel::Warn,
132 log::Level::Info => LogLevel::Info,
133 log::Level::Debug => LogLevel::Debug,
134 log::Level::Trace => LogLevel::Trace,
135 }
136 }
137}
138
139#[sabi_trait]
141pub trait PhaneronLoggingContext: Send + Sync + Clone {
142 fn log(&self, level: LogLevel, message: RString);
143}
144
145#[repr(C)]
147#[derive(StableAbi)]
148#[sabi(kind(Prefix(prefix_ref = PhaneronPluginRootModuleRef)))]
149#[sabi(missing_field(panic))]
150pub struct PhaneronPluginRootModule {
151 #[sabi(last_prefix_field)]
152 pub load:
153 extern "C" fn(load_context: PhaneronPluginContext) -> RResult<PhaneronPlugin, RString>,
154}
155
156impl RootModule for PhaneronPluginRootModuleRef {
157 declare_root_module_statics! {PhaneronPluginRootModuleRef}
158 const BASE_NAME: &'static str = "phaneron-plugin";
159 const NAME: &'static str = "phaneron-plugin";
160 const VERSION_STRINGS: VersionStrings = package_version_strings!();
161}
162
163#[repr(C)]
165#[derive(Clone, StableAbi)]
166pub struct VideoFrameWithId {
167 pub output_id: VideoOutputId,
168 pub frame: types::VideoFrame,
169}
170
171impl VideoFrameWithId {
172 pub fn new(output_id: VideoOutputId, frame: types::VideoFrame) -> Self {
173 Self { output_id, frame }
174 }
175}
176
177#[repr(C)]
179#[derive(Clone, StableAbi)]
180pub struct AudioFrameWithId {
181 pub output_id: AudioOutputId,
182 pub frame: types::AudioFrame,
183}
184
185impl AudioFrameWithId {
186 pub fn new(output_id: AudioOutputId, frame: types::AudioFrame) -> Self {
187 Self { output_id, frame }
188 }
189}
190
191#[repr(C)]
196#[derive(Default, StableAbi)]
197pub struct ShaderParams {
198 params: RVec<ShaderParam>,
199}
200impl ShaderParams {
201 pub fn set_param_video_frame_input(&mut self, video_frame: types::VideoFrame) {
202 self.params.push(ShaderParam::VideoFrameInput(video_frame));
203 }
204
205 pub fn set_param_u32_input(&mut self, val: u32) {
206 self.params.push(ShaderParam::U32Input(val));
207 }
208
209 pub fn set_param_f32_input(&mut self, val: f32) {
210 self.params.push(ShaderParam::F32Input(val));
211 }
212
213 pub fn set_param_video_frame_output(&mut self, width: usize, height: usize) {
215 self.params
216 .push(ShaderParam::VideoFrameOutput { width, height });
217 }
218
219 pub fn get_params(&self) -> &RVec<ShaderParam> {
220 &self.params
221 }
222}
223
224#[repr(C)]
226#[derive(StableAbi)]
227pub enum ShaderParam {
228 VideoFrameInput(types::VideoFrame),
229 U32Input(u32),
230 F32Input(f32),
231 VideoFrameOutput { width: usize, height: usize },
232}
233
234pub struct PluginLogger {
237 context: PhaneronLoggingContext_TO<'static, RBox<()>>,
238}
239
240impl PluginLogger {
241 pub fn init(&'static self) -> Result<(), SetLoggerError> {
245 log::set_logger(self)?;
246 log::set_max_level(LevelFilter::Trace);
247 Ok(())
248 }
249}
250
251impl Debug for PluginLogger {
252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253 f.debug_struct("PluginLogger").finish()
254 }
255}
256
257impl log::Log for PluginLogger {
258 fn enabled(&self, _metadata: &log::Metadata) -> bool {
259 true
260 }
261
262 fn log(&self, record: &log::Record) {
263 if self.enabled(record.metadata()) {
264 self.context
265 .log(record.level().into(), record.args().to_string().into())
266 }
267 }
268
269 fn flush(&self) {}
270}
271
272pub fn get_logger(context: &PhaneronPluginContext) -> &'static PluginLogger {
274 let logger = PluginLogger {
275 context: context.logging_context.clone(),
276 };
277 LOGGER.set(logger).unwrap();
278 LOGGER.get().unwrap()
279}