openh264_sys2/
lib.rs

1#![allow(non_snake_case)]
2#![allow(unpredictable_function_pointer_comparisons)]
3#![allow(non_camel_case_types)]
4#![allow(non_upper_case_globals)]
5#![allow(clippy::missing_safety_doc)]
6#![doc = include_str!("../README.md")]
7
8mod error;
9
10/// Generated bindings for OpenH264.
11mod generated {
12    pub mod consts;
13    #[cfg(feature = "libloading")]
14    pub mod fns_libloading;
15    #[cfg(feature = "source")]
16    pub mod fns_source;
17    pub mod types;
18}
19
20pub use self::generated::consts::*;
21pub use self::generated::types::*;
22pub use error::Error;
23use std::os::raw::{c_int, c_long};
24
25/// Abstraction over `source` or `libloading` APIs.
26#[rustfmt::skip]
27#[allow(clippy::missing_safety_doc)]
28pub trait API {
29    unsafe fn WelsCreateSVCEncoder(&self, ppEncoder: *mut *mut ISVCEncoder) -> ::std::os::raw::c_int;
30    unsafe fn WelsDestroySVCEncoder(&self, pEncoder: *mut ISVCEncoder);
31    unsafe fn WelsGetDecoderCapability(&self, pDecCapability: *mut SDecoderCapability) -> ::std::os::raw::c_int;
32    unsafe fn WelsCreateDecoder(&self, ppDecoder: *mut *mut ISVCDecoder) -> ::std::os::raw::c_long;
33    unsafe fn WelsDestroyDecoder(&self, pDecoder: *mut ISVCDecoder);
34    unsafe fn WelsGetCodecVersion(&self) -> OpenH264Version;
35    unsafe fn WelsGetCodecVersionEx(&self, pVersion: *mut OpenH264Version);
36}
37
38/// API surface via libloading.
39///
40/// While this is no legal advice, the idea is that using this API might be covered by Cisco's [promise to cover MPEG-LA license costs](https://www.openh264.org/).
41/// The big downside is you will have to download binary blobs from Cisco during installation. From [their FAQ](https://www.openh264.org/faq.html) (copied 2024-01-06, emphasis ours):
42///
43/// - **Q: If I use the source code in my product, and then distribute that product on my own, will Cisco cover the MPEG LA licensing fees which I'd otherwise have to pay?**
44///
45///     A: No. Cisco is only covering the licensing fees for its own binary module, and products or projects that utilize it **must download it at the time the product or project is installed on the user's computer or device**. Cisco will not be liable for any licensing fees incurred by other parties.
46///
47/// In addition, note that this might not cover _all_ possible license claims:
48///
49/// - **Q: Is Cisco guaranteeing that it will pay other licensing fees for H.264, should additional patent holders assert claims in the future?**
50///
51///     A: Cisco is providing no such guarantee. We are only covering the royalties that would apply to the binary module under MPEG LA's AVC/H.264 patent pool.
52#[cfg(feature = "libloading")]
53pub mod libloading {
54    pub use crate::generated::fns_libloading::*;
55    use crate::{ISVCDecoder, ISVCEncoder, OpenH264Version, SDecoderCapability};
56    use std::os::raw::{c_int, c_long};
57
58    #[rustfmt::skip]
59    impl super::API for APILoader {
60        unsafe fn WelsCreateSVCEncoder(&self, ppEncoder: *mut *mut ISVCEncoder) -> c_int { APILoader::WelsCreateSVCEncoder(self, ppEncoder) }
61        unsafe fn WelsDestroySVCEncoder(&self, pEncoder: *mut ISVCEncoder) { APILoader::WelsDestroySVCEncoder(self, pEncoder) }
62        unsafe fn WelsGetDecoderCapability(&self, pDecCapability: *mut SDecoderCapability) -> c_int { APILoader::WelsGetDecoderCapability(self, pDecCapability) }
63        unsafe fn WelsCreateDecoder(&self, ppDecoder: *mut *mut ISVCDecoder) -> c_long { APILoader::WelsCreateDecoder(self, ppDecoder) }
64        unsafe fn WelsDestroyDecoder(&self, pDecoder: *mut ISVCDecoder) { APILoader::WelsDestroyDecoder(self, pDecoder) }
65        unsafe fn WelsGetCodecVersion(&self) -> OpenH264Version { APILoader::WelsGetCodecVersion(self) }
66        unsafe fn WelsGetCodecVersionEx(&self, pVersion: *mut OpenH264Version) {APILoader::WelsGetCodecVersionEx(self, pVersion) }
67    }
68}
69
70/// API surface using built-in source.
71///
72/// This API surface should _just work_ once compiled. Depending on your commercial, legal and geographic situation, and the H.264 features you use,
73/// this might or might not come with an elevated patent risk.
74#[cfg(feature = "source")]
75pub mod source {
76    use crate::{ISVCDecoder, ISVCEncoder, OpenH264Version, SDecoderCapability};
77    use std::os::raw::{c_int, c_long};
78
79    #[derive(Debug, Default)]
80    pub struct APILoader;
81
82    #[rustfmt::skip]
83    #[allow(clippy::missing_safety_doc)]
84    impl APILoader {
85        pub const fn new() -> Self { Self }
86        pub unsafe fn WelsCreateSVCEncoder(ppEncoder: *mut *mut ISVCEncoder) -> ::std::os::raw::c_int { unsafe { crate::generated::fns_source::WelsCreateSVCEncoder(ppEncoder) }}
87        pub unsafe fn WelsDestroySVCEncoder(pEncoder: *mut ISVCEncoder) { unsafe { crate::generated::fns_source::WelsDestroySVCEncoder(pEncoder) }}
88        pub unsafe fn WelsGetDecoderCapability(pDecCapability: *mut SDecoderCapability) -> ::std::os::raw::c_int { unsafe { crate::generated::fns_source::WelsGetDecoderCapability(pDecCapability) }}
89        pub unsafe fn WelsCreateDecoder(ppDecoder: *mut *mut ISVCDecoder) -> ::std::os::raw::c_long { unsafe { crate::generated::fns_source::WelsCreateDecoder(ppDecoder) }}
90        pub unsafe fn WelsDestroyDecoder(pDecoder: *mut ISVCDecoder) { unsafe { crate::generated::fns_source::WelsDestroyDecoder(pDecoder) }}
91        pub unsafe fn WelsGetCodecVersion() -> OpenH264Version { unsafe { crate::generated::fns_source::WelsGetCodecVersion() }}
92        pub unsafe fn WelsGetCodecVersionEx(pVersion: *mut OpenH264Version) { unsafe { crate::generated::fns_source::WelsGetCodecVersionEx(pVersion) }}
93    }
94
95    #[rustfmt::skip]
96    #[allow(clippy::missing_safety_doc)]
97    impl super::API for APILoader {
98        unsafe fn WelsCreateSVCEncoder(&self, ppEncoder: *mut *mut ISVCEncoder) -> c_int { unsafe { APILoader::WelsCreateSVCEncoder(ppEncoder) }}
99        unsafe fn WelsDestroySVCEncoder(&self, pEncoder: *mut ISVCEncoder) { unsafe { APILoader::WelsDestroySVCEncoder(pEncoder) }}
100        unsafe fn WelsGetDecoderCapability(&self, pDecCapability: *mut SDecoderCapability) -> c_int { unsafe { APILoader::WelsGetDecoderCapability(pDecCapability) }}
101        unsafe fn WelsCreateDecoder(&self, ppDecoder: *mut *mut ISVCDecoder) -> c_long { unsafe { APILoader::WelsCreateDecoder(ppDecoder) }}
102        unsafe fn WelsDestroyDecoder(&self, pDecoder: *mut ISVCDecoder) { unsafe { APILoader::WelsDestroyDecoder(pDecoder) }}
103        unsafe fn WelsGetCodecVersion(&self) -> OpenH264Version { unsafe { APILoader::WelsGetCodecVersion() }}
104        unsafe fn WelsGetCodecVersionEx(&self, pVersion: *mut OpenH264Version) { unsafe { APILoader::WelsGetCodecVersionEx(pVersion) }}
105    }
106}
107
108/// Convenience wrapper around `libloading` and `source` API surfaces.
109///
110/// This type mainly exists to prevent infecting the rest of the OpenH264 crate with generics. The dispatch overhead
111/// in contrast to H.264 computation is absolutely negligible.
112pub enum DynamicAPI {
113    #[cfg(feature = "source")]
114    Source(source::APILoader),
115
116    #[cfg(feature = "libloading")]
117    Libloading(libloading::APILoader),
118}
119
120impl DynamicAPI {
121    /// Creates an OpenH264 API using the built-in source if available.
122    #[cfg(feature = "source")]
123    pub const fn from_source() -> Self {
124        let api = crate::source::APILoader;
125        Self::Source(api)
126    }
127
128    /// Creates an OpenH264 API via the provided shared library.
129    ///
130    /// In order for this to have any (legal) use, you should download the library from
131    /// Cisco [**during installation**](https://www.openh264.org/faq.html), and then
132    /// pass the file-system path in here.
133    ///
134    /// # Errors
135    ///
136    /// Can fail if the library could not be loaded, e.g., it does not exist.
137    ///
138    /// # Safety
139    ///
140    /// Will cause UB if the provided path does not match the current platform and version.
141    #[cfg(feature = "libloading")]
142    pub unsafe fn from_blob_path_unchecked(path: impl AsRef<std::ffi::OsStr>) -> Result<Self, Error> {
143        let api = unsafe { libloading::APILoader::new(path)? };
144        Ok(Self::Libloading(api))
145    }
146
147    /// Creates an OpenH264 API via the provided shared library if the library is well-known.
148    ///
149    /// In order for this to have any (legal) use, you should download the library from
150    /// Cisco [**during installation**](https://www.openh264.org/faq.html), and then
151    /// pass the file-system path in here.
152    ///
153    /// This function also checks the file's SHA against a list of well-known libraries we can load.
154    ///
155    /// # Errors
156    ///
157    /// Can fail if the library could not be loaded, e.g., it does not exist. It can also fail if the file's SHA does
158    /// not match against a list of well-known versions we can load.
159    #[cfg(feature = "libloading")]
160    pub fn from_blob_path(path: impl AsRef<std::ffi::OsStr>) -> Result<Self, Error> {
161        use sha2::Digest;
162        use std::fmt::Write;
163
164        let bytes = std::fs::read(path.as_ref())?;
165
166        // Get SHA of blob at given path.
167        let sha256 = sha2::Sha256::digest(bytes).iter().fold(String::new(), |mut acc, byte| {
168            write!(&mut acc, "{:02x}", byte).unwrap(); // Unless we're out of memory this should never panic.
169            acc
170        });
171
172        // Check all known hashes if we should load this library.
173        // TODO: We might also want to verify this matches our architecture, but then again libloading should catch that.
174        let hash_is_well_known = include_str!("blobs/hashes.txt")
175            .lines()
176            .filter_map(|line| line.split_whitespace().next())
177            .any(|x| x == sha256);
178
179        if !hash_is_well_known {
180            return Err(Error::InvalidHash(sha256));
181        }
182
183        unsafe { Self::from_blob_path_unchecked(path) }
184    }
185}
186
187#[allow(unreachable_patterns)]
188#[allow(unused)]
189impl API for DynamicAPI {
190    unsafe fn WelsCreateSVCEncoder(&self, ppEncoder: *mut *mut ISVCEncoder) -> c_int {
191        unsafe {
192            match self {
193                #[cfg(feature = "source")]
194                DynamicAPI::Source(api) => api.WelsCreateSVCEncoder(ppEncoder),
195                #[cfg(feature = "libloading")]
196                DynamicAPI::Libloading(api) => api.WelsCreateSVCEncoder(ppEncoder),
197                _ => panic!("No API enabled"),
198            }
199        }
200    }
201
202    unsafe fn WelsDestroySVCEncoder(&self, pEncoder: *mut ISVCEncoder) {
203        unsafe {
204            match self {
205                #[cfg(feature = "source")]
206                DynamicAPI::Source(api) => api.WelsDestroySVCEncoder(pEncoder),
207                #[cfg(feature = "libloading")]
208                DynamicAPI::Libloading(api) => api.WelsDestroySVCEncoder(pEncoder),
209                _ => panic!("No API enabled"),
210            }
211        }
212    }
213
214    unsafe fn WelsGetDecoderCapability(&self, pDecCapability: *mut SDecoderCapability) -> c_int {
215        unsafe {
216            match self {
217                #[cfg(feature = "source")]
218                DynamicAPI::Source(api) => api.WelsGetDecoderCapability(pDecCapability),
219                #[cfg(feature = "libloading")]
220                DynamicAPI::Libloading(api) => api.WelsGetDecoderCapability(pDecCapability),
221                _ => panic!("No API enabled"),
222            }
223        }
224    }
225
226    unsafe fn WelsCreateDecoder(&self, ppDecoder: *mut *mut ISVCDecoder) -> c_long {
227        unsafe {
228            match self {
229                #[cfg(feature = "source")]
230                DynamicAPI::Source(api) => api.WelsCreateDecoder(ppDecoder),
231                #[cfg(feature = "libloading")]
232                DynamicAPI::Libloading(api) => api.WelsCreateDecoder(ppDecoder),
233                _ => panic!("No API enabled"),
234            }
235        }
236    }
237
238    unsafe fn WelsDestroyDecoder(&self, pDecoder: *mut ISVCDecoder) {
239        unsafe {
240            match self {
241                #[cfg(feature = "source")]
242                DynamicAPI::Source(api) => api.WelsDestroyDecoder(pDecoder),
243                #[cfg(feature = "libloading")]
244                DynamicAPI::Libloading(api) => api.WelsDestroyDecoder(pDecoder),
245                _ => panic!("No API enabled"),
246            }
247        }
248    }
249
250    unsafe fn WelsGetCodecVersion(&self) -> OpenH264Version {
251        unsafe {
252            match self {
253                #[cfg(feature = "source")]
254                DynamicAPI::Source(api) => api.WelsGetCodecVersion(),
255                #[cfg(feature = "libloading")]
256                DynamicAPI::Libloading(api) => api.WelsGetCodecVersion(),
257                _ => panic!("No API enabled"),
258            }
259        }
260    }
261
262    unsafe fn WelsGetCodecVersionEx(&self, pVersion: *mut OpenH264Version) {
263        unsafe {
264            match self {
265                #[cfg(feature = "source")]
266                DynamicAPI::Source(api) => api.WelsGetCodecVersionEx(pVersion),
267                #[cfg(feature = "libloading")]
268                DynamicAPI::Libloading(api) => api.WelsGetCodecVersionEx(pVersion),
269                _ => panic!("No API enabled"),
270            }
271        }
272    }
273}
274
275/// Helper function that should always give the name of the latest supported and
276/// included DLL file, used by unit tests.
277#[doc(hidden)]
278#[cfg(all(target_os = "windows", target_arch = "x86_64", feature = "libloading"))]
279pub fn reference_dll_name() -> &'static str {
280    include_str!("../tests/reference/reference.txt")
281}