char/char-rtc-obs

git clone https://git.t4t.associates/char/char-rtc-obs

videogame hackerInitial commiteeaf4aa

main
3.9 KiB127 linesraw
1use env_logger::filter::{Builder, Filter};
2use log::{Level, Log};
3use std::{ffi::CStr, sync::atomic::AtomicBool, sync::atomic::Ordering};
4
5static DEBUG_WHIP: AtomicBool = AtomicBool::new(false);
6
7pub(crate) fn debug_whip() -> bool {
8    DEBUG_WHIP.load(Ordering::Relaxed)
9}
10
11/// cbindgen:ignore
12mod obs {
13    use std::os::raw::c_char;
14    use std::os::raw::c_int;
15
16    #[repr(C)]
17    #[derive(Debug)]
18    pub(super) struct obs_cmdline_args {
19        pub(super) argc: c_int,
20        pub(super) argv: *const *const c_char,
21    }
22
23    #[link(name = "libobs")]
24    extern "C" {
25        pub(super) fn blog(log_level: i32, format: *const c_char, ...);
26        pub(super) fn obs_get_cmdline_args() -> obs_cmdline_args;
27    }
28}
29
30pub struct OBSLogger {
31    log_filter: Filter,
32}
33
34impl OBSLogger {
35    pub fn install() {
36        // Pull our RUST_LOG in case they are using that
37        let mut log_filter: String = std::env::var("RUST_LOG").unwrap_or_else(|_| "INFO".into());
38
39        unsafe {
40            let raw_args = obs::obs_get_cmdline_args();
41            let mut args = std::slice::from_raw_parts(raw_args.argv, raw_args.argc as usize)
42                .iter()
43                .map(|arg| CStr::from_ptr(*arg).to_string_lossy());
44
45            while let Some(arg) = args.next() {
46                match arg.as_ref() {
47                    "--debug-webrtc-whip" => {
48                        DEBUG_WHIP.store(true, Ordering::Relaxed);
49                    }
50                    "--debug-webrtc-filter" => {
51                        if let Some(filter) = args.next() {
52                            log_filter = filter.into();
53                        }
54                    }
55                    _ => {}
56                }
57            }
58            let mut filter_builder = Builder::new();
59            log::set_boxed_logger(Box::new(Self {
60                log_filter: filter_builder.parse(&log_filter).build(),
61            }))
62            .expect("Logger already set");
63
64            log::set_max_level(log::LevelFilter::Trace);
65        }
66    }
67}
68
69impl Log for OBSLogger {
70    fn enabled(&self, metadata: &log::Metadata) -> bool {
71        self.log_filter.enabled(metadata)
72    }
73
74    fn log(&self, record: &log::Record) {
75        if !self.log_filter.matches(record) {
76            return;
77        }
78
79        let level = record.level();
80        let log_level = match level {
81            Level::Trace => (300, "[T] "),
82            Level::Debug => (300, "[D] "),
83            Level::Info => (300, ""),
84            Level::Warn => (200, ""),
85            Level::Error => (100, ""),
86        };
87
88        let message_prefix = format!(
89            "{}[{}:{}]",
90            log_level.1,
91            if record.target().is_empty() {
92                record.module_path().unwrap_or("unk")
93            } else {
94                record.target()
95            },
96            record.line().unwrap_or(0),
97        );
98
99        const MAX_CHUNK_SIZE: usize = 3500;
100
101        let message_chars = record.args().to_string().chars().collect::<Vec<char>>();
102        let chunks = message_chars.chunks(MAX_CHUNK_SIZE);
103        let chunks_len = chunks.len();
104        for (index, chunk) in chunks.enumerate() {
105            unsafe {
106                let chunk = chunk.iter().cloned().collect::<String>();
107                if chunks_len == 1 {
108                    obs::blog(
109                        log_level.0,
110                        format!("{message_prefix}: {chunk}\0").as_ptr() as *const i8,
111                    );
112                } else {
113                    obs::blog(
114                        log_level.0,
115                        format!(
116                            "{message_prefix}: MULTIPART [{}/{chunks_len}] {chunk}\0",
117                            index + 1,
118                        )
119                        .as_ptr() as *const i8,
120                    );
121                }
122            }
123        }
124    }
125
126    fn flush(&self) {}
127}