Skip to main content

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 { unsafe { APILoader::WelsCreateSVCEncoder(self, ppEncoder) } }
61        unsafe fn WelsDestroySVCEncoder(&self, pEncoder: *mut ISVCEncoder) { unsafe { APILoader::WelsDestroySVCEncoder(self, pEncoder) } }
62        unsafe fn WelsGetDecoderCapability(&self, pDecCapability: *mut SDecoderCapability) -> c_int { unsafe { APILoader::WelsGetDecoderCapability(self, pDecCapability) } }
63        unsafe fn WelsCreateDecoder(&self, ppDecoder: *mut *mut ISVCDecoder) -> c_long { unsafe { APILoader::WelsCreateDecoder(self, ppDecoder) } }
64        unsafe fn WelsDestroyDecoder(&self, pDecoder: *mut ISVCDecoder) { unsafe { APILoader::WelsDestroyDecoder(self, pDecoder) } }
65        unsafe fn WelsGetCodecVersion(&self) -> OpenH264Version { unsafe { APILoader::WelsGetCodecVersion(self) } }
66        unsafe fn WelsGetCodecVersionEx(&self, pVersion: *mut OpenH264Version) { unsafe { 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.
112#[allow(clippy::large_enum_variant)]
113pub enum DynamicAPI {
114    #[cfg(feature = "source")]
115    Source(source::APILoader),
116
117    #[cfg(feature = "libloading")]
118    Libloading(libloading::APILoader),
119}
120
121impl DynamicAPI {
122    /// Creates an OpenH264 API using the built-in source if available.
123    #[cfg(feature = "source")]
124    pub const fn from_source() -> Self {
125        let api = crate::source::APILoader;
126        Self::Source(api)
127    }
128
129    /// Creates an OpenH264 API via the provided shared library.
130    ///
131    /// In order for this to have any (legal) use, you should download the library from
132    /// Cisco [**during installation**](https://www.openh264.org/faq.html), and then
133    /// pass the file-system path in here.
134    ///
135    /// # Errors
136    ///
137    /// Can fail if the library could not be loaded, e.g., it does not exist.
138    ///
139    /// # Safety
140    ///
141    /// Will cause UB if the provided path does not match the current platform and version.
142    #[cfg(feature = "libloading")]
143    pub unsafe fn from_blob_path_unchecked(path: impl AsRef<std::ffi::OsStr>) -> Result<Self, Error> {
144        let api = unsafe { libloading::APILoader::new(path)? };
145        Ok(Self::Libloading(api))
146    }
147
148    /// Creates an OpenH264 API via the provided shared library if the library is well-known.
149    ///
150    /// In order for this to have any (legal) use, you should download the library from
151    /// Cisco [**during installation**](https://www.openh264.org/faq.html), and then
152    /// pass the file-system path in here.
153    ///
154    /// This function also checks the file's SHA against a list of well-known libraries we can load.
155    ///
156    /// # Errors
157    ///
158    /// 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
159    /// not match against a list of well-known versions we can load.
160    #[cfg(feature = "libloading")]
161    pub fn from_blob_path(path: impl AsRef<std::ffi::OsStr>) -> Result<Self, Error> {
162        use sha2::Digest;
163        use std::fmt::Write;
164
165        let bytes = std::fs::read(path.as_ref())?;
166
167        // Get SHA of blob at given path.
168        let sha256 = sha2::Sha256::digest(bytes).iter().fold(String::new(), |mut acc, byte| {
169            write!(&mut acc, "{:02x}", byte).unwrap(); // Unless we're out of memory this should never panic.
170            acc
171        });
172
173        // Check all known hashes if we should load this library.
174        // TODO: We might also want to verify this matches our architecture, but then again libloading should catch that.
175        let hash_is_well_known = include_str!("blobs/hashes.txt")
176            .lines()
177            .filter_map(|line| line.split_whitespace().next())
178            .any(|x| x == sha256);
179
180        if !hash_is_well_known {
181            return Err(Error::InvalidHash(sha256));
182        }
183
184        unsafe { Self::from_blob_path_unchecked(path) }
185    }
186}
187
188#[allow(unreachable_patterns)]
189#[allow(unused)]
190impl API for DynamicAPI {
191    unsafe fn WelsCreateSVCEncoder(&self, ppEncoder: *mut *mut ISVCEncoder) -> c_int {
192        unsafe {
193            match self {
194                #[cfg(feature = "source")]
195                DynamicAPI::Source(api) => api.WelsCreateSVCEncoder(ppEncoder),
196                #[cfg(feature = "libloading")]
197                DynamicAPI::Libloading(api) => api.WelsCreateSVCEncoder(ppEncoder),
198                _ => panic!("No API enabled"),
199            }
200        }
201    }
202
203    unsafe fn WelsDestroySVCEncoder(&self, pEncoder: *mut ISVCEncoder) {
204        unsafe {
205            match self {
206                #[cfg(feature = "source")]
207                DynamicAPI::Source(api) => api.WelsDestroySVCEncoder(pEncoder),
208                #[cfg(feature = "libloading")]
209                DynamicAPI::Libloading(api) => api.WelsDestroySVCEncoder(pEncoder),
210                _ => panic!("No API enabled"),
211            }
212        }
213    }
214
215    unsafe fn WelsGetDecoderCapability(&self, pDecCapability: *mut SDecoderCapability) -> c_int {
216        unsafe {
217            match self {
218                #[cfg(feature = "source")]
219                DynamicAPI::Source(api) => api.WelsGetDecoderCapability(pDecCapability),
220                #[cfg(feature = "libloading")]
221                DynamicAPI::Libloading(api) => api.WelsGetDecoderCapability(pDecCapability),
222                _ => panic!("No API enabled"),
223            }
224        }
225    }
226
227    unsafe fn WelsCreateDecoder(&self, ppDecoder: *mut *mut ISVCDecoder) -> c_long {
228        unsafe {
229            match self {
230                #[cfg(feature = "source")]
231                DynamicAPI::Source(api) => api.WelsCreateDecoder(ppDecoder),
232                #[cfg(feature = "libloading")]
233                DynamicAPI::Libloading(api) => api.WelsCreateDecoder(ppDecoder),
234                _ => panic!("No API enabled"),
235            }
236        }
237    }
238
239    unsafe fn WelsDestroyDecoder(&self, pDecoder: *mut ISVCDecoder) {
240        unsafe {
241            match self {
242                #[cfg(feature = "source")]
243                DynamicAPI::Source(api) => api.WelsDestroyDecoder(pDecoder),
244                #[cfg(feature = "libloading")]
245                DynamicAPI::Libloading(api) => api.WelsDestroyDecoder(pDecoder),
246                _ => panic!("No API enabled"),
247            }
248        }
249    }
250
251    unsafe fn WelsGetCodecVersion(&self) -> OpenH264Version {
252        unsafe {
253            match self {
254                #[cfg(feature = "source")]
255                DynamicAPI::Source(api) => api.WelsGetCodecVersion(),
256                #[cfg(feature = "libloading")]
257                DynamicAPI::Libloading(api) => api.WelsGetCodecVersion(),
258                _ => panic!("No API enabled"),
259            }
260        }
261    }
262
263    unsafe fn WelsGetCodecVersionEx(&self, pVersion: *mut OpenH264Version) {
264        unsafe {
265            match self {
266                #[cfg(feature = "source")]
267                DynamicAPI::Source(api) => api.WelsGetCodecVersionEx(pVersion),
268                #[cfg(feature = "libloading")]
269                DynamicAPI::Libloading(api) => api.WelsGetCodecVersionEx(pVersion),
270                _ => panic!("No API enabled"),
271            }
272        }
273    }
274}
275
276/// Helper function that should always give the name of the latest supported and
277/// included DLL file, used by unit tests.
278#[doc(hidden)]
279#[cfg(all(target_os = "windows", target_arch = "x86_64", feature = "libloading"))]
280pub fn reference_dll_name() -> &'static str {
281    include_str!("../tests/reference/reference.txt")
282}