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}