videostream_sys/lib.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025 Au-Zone Technologies
3
4#![allow(non_upper_case_globals)]
5#![allow(non_camel_case_types)]
6#![allow(non_snake_case)]
7#![allow(clippy::type_complexity)]
8#![allow(clippy::missing_safety_doc)]
9#![allow(clippy::too_many_arguments)]
10
11include!("ffi.rs");
12
13// Re-export libloading for error handling
14pub use libloading;
15
16use std::sync::{Mutex, OnceLock};
17
18// Store a leaked (never-freed) reference to the library to prevent dlclose() at program exit.
19// This prevents segfaults when the C library or GStreamer plugins have global destructors
20// or atexit() handlers that would run after the library is unloaded.
21// Using a leaked Box ensures the library pointer remains valid for the entire program lifetime.
22static LIBRARY: OnceLock<&'static VideoStreamLibrary> = OnceLock::new();
23static INIT_LOCK: Mutex<()> = Mutex::new(());
24
25/// Initialize the VideoStream library by loading libvideostream.so
26///
27/// This must be called before using any other VideoStream functions.
28/// Returns an error if the library cannot be loaded.
29///
30/// The environment variable `VIDEOSTREAM_LIBRARY` can be used to specify
31/// a custom path to the library. If not set, searches standard system paths.
32pub fn init() -> Result<&'static VideoStreamLibrary, libloading::Error> {
33 if let Some(lib) = LIBRARY.get() {
34 return Ok(lib);
35 }
36
37 let _guard = INIT_LOCK.lock().unwrap();
38
39 // Double-check after acquiring lock
40 if let Some(lib) = LIBRARY.get() {
41 return Ok(lib);
42 }
43
44 // Check for VIDEOSTREAM_LIBRARY environment variable
45 let lib_path = std::env::var("VIDEOSTREAM_LIBRARY")
46 .ok()
47 .unwrap_or_else(|| "libvideostream.so".to_string());
48
49 let lib = unsafe { VideoStreamLibrary::new(lib_path.as_str())? };
50
51 // Leak the library to prevent dlclose() at program exit.
52 // This intentionally leaks memory but prevents segfaults from cleanup code
53 // trying to access unloaded library code.
54 let leaked_lib: &'static VideoStreamLibrary = Box::leak(Box::new(lib));
55
56 LIBRARY
57 .set(leaked_lib)
58 .ok()
59 .expect("Failed to initialize library");
60
61 Ok(*LIBRARY.get().unwrap())
62}
63
64/// Get a reference to the loaded library
65///
66/// Panics if init() has not been called successfully.
67pub fn library() -> &'static VideoStreamLibrary {
68 LIBRARY
69 .get()
70 .expect("VideoStream library not initialized - call videostream_sys::init() first")
71}
72
73/// Try to get a reference to the loaded library without panicking
74pub fn try_library() -> Option<&'static VideoStreamLibrary> {
75 LIBRARY.get().copied()
76}