/src/libheif/libheif/init.cc
Line | Count | Source |
1 | | /* |
2 | | * HEIF codec. |
3 | | * Copyright (c) 2022 Dirk Farin <dirk.farin@gmail.com> |
4 | | * |
5 | | * This file is part of libheif. |
6 | | * |
7 | | * libheif is free software: you can redistribute it and/or modify |
8 | | * it under the terms of the GNU Lesser General Public License as |
9 | | * published by the Free Software Foundation, either version 3 of |
10 | | * the License, or (at your option) any later version. |
11 | | * |
12 | | * libheif is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public License |
18 | | * along with libheif. If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include "init.h" |
22 | | #include "libheif/heif.h" |
23 | | #include "error.h" |
24 | | #include "plugin_registry.h" |
25 | | #include "common_utils.h" |
26 | | #include "color-conversion/colorconversion.h" |
27 | | |
28 | | #if ENABLE_MULTITHREADING_SUPPORT |
29 | | |
30 | | #include <mutex> |
31 | | |
32 | | #endif |
33 | | |
34 | | #if defined(_WIN32) |
35 | | #include "plugins_windows.h" |
36 | | #else |
37 | | |
38 | | #include "plugins_unix.h" |
39 | | |
40 | | #endif |
41 | | |
42 | | #define STRINGIFY2(x) #x |
43 | | #define STRINGIFY(x) STRINGIFY2(x) |
44 | | |
45 | | void heif_unload_all_plugins(); |
46 | | |
47 | | #if ENABLE_PLUGIN_LOADING |
48 | | |
49 | | void heif_unregister_encoder_plugin(const heif_encoder_plugin* plugin); |
50 | | |
51 | | std::vector<std::string> get_plugin_paths() |
52 | | { |
53 | | std::vector<std::string> plugin_paths; |
54 | | |
55 | | #if defined(_WIN32) |
56 | | plugin_paths = get_plugin_directories_from_environment_variable_windows(); |
57 | | #else |
58 | | plugin_paths = get_plugin_directories_from_environment_variable_unix(); |
59 | | #endif |
60 | | |
61 | | if (plugin_paths.empty()) { |
62 | | plugin_paths.push_back(LIBHEIF_PLUGIN_DIRECTORY); |
63 | | } |
64 | | |
65 | | return plugin_paths; |
66 | | } |
67 | | |
68 | | std::vector<std::string> list_all_potential_plugins_in_directory(const char* directory) |
69 | | { |
70 | | #if defined(_WIN32) |
71 | | return list_all_potential_plugins_in_directory_windows(directory); |
72 | | #else |
73 | | return list_all_potential_plugins_in_directory_unix(directory); |
74 | | #endif |
75 | | } |
76 | | |
77 | | #else |
78 | | std::vector<std::string> get_plugin_paths() |
79 | 0 | { |
80 | 0 | return {}; |
81 | 0 | } |
82 | | #endif |
83 | | |
84 | | |
85 | | static int heif_library_initialization_count = 0; |
86 | | static bool default_plugins_registered = true; // because they are implicitly registered at startup |
87 | | |
88 | | |
89 | | #if ENABLE_MULTITHREADING_SUPPORT |
90 | | |
91 | | static std::recursive_mutex& heif_init_mutex() |
92 | 9 | { |
93 | 9 | static std::recursive_mutex init_mutex; |
94 | 9 | return init_mutex; |
95 | 9 | } |
96 | | |
97 | | #endif |
98 | | |
99 | | |
100 | | void load_plugins_if_not_initialized_yet() |
101 | 15.5k | { |
102 | 15.5k | if (heif_library_initialization_count == 0) { |
103 | 0 | heif_init(nullptr); |
104 | 0 | } |
105 | 15.5k | } |
106 | | |
107 | | |
108 | | heif_error heif_init(heif_init_params*) |
109 | 9 | { |
110 | 9 | #if ENABLE_MULTITHREADING_SUPPORT |
111 | 9 | std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); |
112 | 9 | #endif |
113 | | |
114 | 9 | if (heif_library_initialization_count == 0) { |
115 | | |
116 | 9 | ColorConversionPipeline::init_ops(); |
117 | | |
118 | | // --- initialize builtin plugins |
119 | | |
120 | 9 | if (!default_plugins_registered) { |
121 | 0 | register_default_plugins(); |
122 | 0 | } |
123 | | |
124 | | #if ENABLE_PLUGIN_LOADING |
125 | | struct heif_error err{}; |
126 | | std::vector<std::string> plugin_paths = get_plugin_paths(); |
127 | | |
128 | | for (const auto& dir : plugin_paths) { |
129 | | err = heif_load_plugins(dir.c_str(), nullptr, nullptr, 0); |
130 | | if (err.code != 0) { |
131 | | return err; |
132 | | } |
133 | | } |
134 | | #endif |
135 | 9 | } |
136 | | |
137 | | // Note: it is important that we increase the counter AFTER initialization such that |
138 | | // 'load_plugins_if_not_initialized_yet()' can check this without having to lock the mutex. |
139 | 9 | heif_library_initialization_count++; |
140 | | |
141 | 9 | return {heif_error_Ok, heif_suberror_Unspecified, Error::kSuccess}; |
142 | 9 | } |
143 | | |
144 | | |
145 | | void heif_deinit() |
146 | 0 | { |
147 | 0 | #if ENABLE_MULTITHREADING_SUPPORT |
148 | 0 | std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); |
149 | 0 | #endif |
150 | |
|
151 | 0 | if (heif_library_initialization_count == 0) { |
152 | | // This case should never happen (heif_deinit() is called more often than heif_init()). |
153 | 0 | return; |
154 | 0 | } |
155 | | |
156 | 0 | if (heif_library_initialization_count == 1) { |
157 | 0 | heif_unregister_decoder_plugins(); |
158 | 0 | heif_unregister_encoder_plugins(); |
159 | 0 | default_plugins_registered = false; |
160 | |
|
161 | 0 | heif_unload_all_plugins(); |
162 | |
|
163 | 0 | ColorConversionPipeline::release_ops(); |
164 | 0 | } |
165 | | |
166 | | // Note: contrary to heif_init() I think it does not matter whether we decrease the counter before or after deinitialization. |
167 | | // If the client application calls heif_deinit() in parallel to some other libheif function, it is really broken. |
168 | 0 | heif_library_initialization_count--; |
169 | 0 | } |
170 | | |
171 | | |
172 | | // This could be inside ENABLE_PLUGIN_LOADING, but the "include-what-you-use" checker cannot process this. |
173 | | #include <vector> |
174 | | #include <string> |
175 | | #include <cstring> |
176 | | |
177 | | #if ENABLE_PLUGIN_LOADING |
178 | | |
179 | | #if defined(_WIN32) |
180 | | typedef PluginLibrary_Windows PluginLibrary_SysDep; |
181 | | #else |
182 | | typedef PluginLibrary_Unix PluginLibrary_SysDep; |
183 | | #endif |
184 | | |
185 | | |
186 | | struct loaded_plugin |
187 | | { |
188 | | PluginLibrary_SysDep plugin_library_handle; |
189 | | struct heif_plugin_info* info = nullptr; |
190 | | int openCnt = 0; |
191 | | }; |
192 | | |
193 | | static std::vector<loaded_plugin> sLoadedPlugins; |
194 | | |
195 | | MAYBE_UNUSED heif_error error_dlopen{heif_error_Plugin_loading_error, heif_suberror_Plugin_loading_error, "Cannot open plugin (dlopen)."}; |
196 | | MAYBE_UNUSED heif_error error_plugin_not_loaded{heif_error_Plugin_loading_error, heif_suberror_Plugin_is_not_loaded, "Trying to remove a plugin that is not loaded."}; |
197 | | MAYBE_UNUSED heif_error error_cannot_read_plugin_directory{heif_error_Plugin_loading_error, heif_suberror_Cannot_read_plugin_directory, "Cannot read plugin directory."}; |
198 | | |
199 | | MAYBE_UNUSED static void unregister_plugin(const heif_plugin_info* info) |
200 | | { |
201 | | switch (info->type) { |
202 | | case heif_plugin_type_encoder: { |
203 | | auto* encoder_plugin = static_cast<const heif_encoder_plugin*>(info->plugin); |
204 | | heif_unregister_encoder_plugin(encoder_plugin); |
205 | | break; |
206 | | } |
207 | | case heif_plugin_type_decoder: { |
208 | | // TODO |
209 | | } |
210 | | } |
211 | | } |
212 | | |
213 | | |
214 | | struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin) |
215 | | { |
216 | | #if ENABLE_MULTITHREADING_SUPPORT |
217 | | std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); |
218 | | #endif |
219 | | |
220 | | PluginLibrary_SysDep plugin; |
221 | | auto err = plugin.load_from_file(filename); |
222 | | if (err.code) { |
223 | | return err; |
224 | | } |
225 | | |
226 | | heif_plugin_info* plugin_info = plugin.get_plugin_info(); |
227 | | |
228 | | // --- check whether the plugin is already loaded |
229 | | // If yes, return pointer to existing plugin. |
230 | | |
231 | | for (auto& p : sLoadedPlugins) { |
232 | | if (p.plugin_library_handle == plugin) { |
233 | | if (out_plugin) { |
234 | | *out_plugin = p.info; |
235 | | p.openCnt++; |
236 | | return heif_error_ok; |
237 | | } |
238 | | } |
239 | | } |
240 | | |
241 | | loaded_plugin loadedPlugin; |
242 | | loadedPlugin.plugin_library_handle = plugin; |
243 | | loadedPlugin.openCnt = 1; |
244 | | loadedPlugin.info = plugin_info; |
245 | | sLoadedPlugins.push_back(loadedPlugin); |
246 | | |
247 | | *out_plugin = plugin_info; |
248 | | |
249 | | switch (loadedPlugin.info->type) { |
250 | | case heif_plugin_type_encoder: { |
251 | | auto* encoder_plugin = static_cast<const heif_encoder_plugin*>(plugin_info->plugin); |
252 | | if (encoder_plugin->plugin_api_version < heif_encoder_plugin_minimum_version) { |
253 | | return {heif_error_Plugin_loading_error, |
254 | | heif_suberror_Unsupported_plugin_version, |
255 | | "Encoder plugin needs to be at least version " STRINGIFY(heif_encoder_plugin_latest_version) |
256 | | }; |
257 | | } |
258 | | |
259 | | if (encoder_plugin->plugin_api_version >= 4 && |
260 | | encoder_plugin->minimum_required_libheif_version > heif_get_version_number()) { |
261 | | return { |
262 | | heif_error_Plugin_loading_error, |
263 | | heif_suberror_Unsupported_plugin_version, |
264 | | "Encoder plugin requires at least libheif version " LIBHEIF_VERSION |
265 | | }; |
266 | | } |
267 | | |
268 | | struct heif_error err = heif_register_encoder_plugin(encoder_plugin); |
269 | | if (err.code) { |
270 | | return err; |
271 | | } |
272 | | break; |
273 | | } |
274 | | |
275 | | case heif_plugin_type_decoder: { |
276 | | auto* decoder_plugin = static_cast<const heif_decoder_plugin*>(plugin_info->plugin); |
277 | | if (decoder_plugin->plugin_api_version < heif_decoder_plugin_minimum_version) { |
278 | | return {heif_error_Plugin_loading_error, |
279 | | heif_suberror_Unsupported_plugin_version, |
280 | | "Decoder plugin needs to be at least version " STRINGIFY(heif_decoder_plugin_latest_version) |
281 | | }; |
282 | | } |
283 | | |
284 | | if (decoder_plugin->plugin_api_version >= 4 && |
285 | | decoder_plugin->minimum_required_libheif_version > heif_get_version_number()) { |
286 | | return { |
287 | | heif_error_Plugin_loading_error, |
288 | | heif_suberror_Unsupported_plugin_version, |
289 | | "Decoder plugin requires at least libheif version " LIBHEIF_VERSION |
290 | | }; |
291 | | } |
292 | | |
293 | | struct heif_error err = heif_register_decoder_plugin(decoder_plugin); |
294 | | if (err.code) { |
295 | | return err; |
296 | | } |
297 | | break; |
298 | | } |
299 | | } |
300 | | |
301 | | return heif_error_ok; |
302 | | } |
303 | | |
304 | | struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin) |
305 | | { |
306 | | #if ENABLE_MULTITHREADING_SUPPORT |
307 | | std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); |
308 | | #endif |
309 | | |
310 | | for (size_t i = 0; i < sLoadedPlugins.size(); i++) { |
311 | | auto& p = sLoadedPlugins[i]; |
312 | | |
313 | | if (p.info == plugin) { |
314 | | p.plugin_library_handle.release(); |
315 | | p.openCnt--; |
316 | | |
317 | | if (p.openCnt == 0) { |
318 | | unregister_plugin(plugin); |
319 | | |
320 | | sLoadedPlugins[i] = sLoadedPlugins.back(); |
321 | | sLoadedPlugins.pop_back(); |
322 | | } |
323 | | |
324 | | return heif_error_ok; |
325 | | } |
326 | | } |
327 | | |
328 | | return error_plugin_not_loaded; |
329 | | } |
330 | | |
331 | | void heif_unload_all_plugins() |
332 | | { |
333 | | #if ENABLE_MULTITHREADING_SUPPORT |
334 | | std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); |
335 | | #endif |
336 | | |
337 | | for (auto& p : sLoadedPlugins) { |
338 | | unregister_plugin(p.info); |
339 | | |
340 | | for (int i = 0; i < p.openCnt; i++) { |
341 | | p.plugin_library_handle.release(); |
342 | | } |
343 | | } |
344 | | |
345 | | sLoadedPlugins.clear(); |
346 | | } |
347 | | |
348 | | |
349 | | struct heif_error heif_load_plugins(const char* directory, |
350 | | const struct heif_plugin_info** out_plugins, |
351 | | int* out_nPluginsLoaded, |
352 | | int output_array_size) |
353 | | { |
354 | | auto libraryFiles = list_all_potential_plugins_in_directory(directory); |
355 | | |
356 | | int nPlugins = 0; |
357 | | |
358 | | // Loading the plugins may return several errors, but we can only return one of them, |
359 | | // which is remembered here. |
360 | | heif_error err_result = heif_error_ok; |
361 | | |
362 | | for (const auto& filename : libraryFiles) { |
363 | | const struct heif_plugin_info* info = nullptr; |
364 | | auto err = heif_load_plugin(filename.c_str(), &info); |
365 | | if (err.code == 0) { |
366 | | if (out_plugins) { |
367 | | if (nPlugins == output_array_size) { |
368 | | break; |
369 | | } |
370 | | |
371 | | out_plugins[nPlugins] = info; |
372 | | } |
373 | | |
374 | | nPlugins++; |
375 | | } |
376 | | else { |
377 | | // remember error |
378 | | err_result = err; |
379 | | } |
380 | | } |
381 | | |
382 | | if (nPlugins < output_array_size && out_plugins) { |
383 | | out_plugins[nPlugins] = nullptr; |
384 | | } |
385 | | |
386 | | if (out_nPluginsLoaded) { |
387 | | *out_nPluginsLoaded = nPlugins; |
388 | | } |
389 | | |
390 | | return err_result; |
391 | | } |
392 | | |
393 | | #else |
394 | | static heif_error heif_error_plugins_unsupported{heif_error_Unsupported_feature, heif_suberror_Unspecified, "Plugins are not supported"}; |
395 | | |
396 | | struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin) |
397 | 0 | { |
398 | 0 | return heif_error_plugins_unsupported; |
399 | 0 | } |
400 | | |
401 | | |
402 | | struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin) |
403 | 0 | { |
404 | 0 | return heif_error_plugins_unsupported; |
405 | 0 | } |
406 | | |
407 | | |
408 | 0 | void heif_unload_all_plugins() {} |
409 | | |
410 | | struct heif_error heif_load_plugins(const char* directory, |
411 | | const struct heif_plugin_info** out_plugins, |
412 | | int* out_nPluginsLoaded, |
413 | | int output_array_size) |
414 | 0 | { |
415 | 0 | if (out_nPluginsLoaded) { |
416 | 0 | *out_nPluginsLoaded = 0; |
417 | 0 | } |
418 | |
|
419 | 0 | return heif_error_plugins_unsupported; |
420 | 0 | } |
421 | | |
422 | | #endif |
423 | | |
424 | | |
425 | | const char* const* heif_get_plugin_directories() |
426 | 0 | { |
427 | 0 | auto plugin_paths = get_plugin_paths(); |
428 | 0 | size_t n = plugin_paths.size(); |
429 | |
|
430 | 0 | auto out_paths = new char* [n + 1]; |
431 | 0 | for (size_t i = 0; i < n; i++) { |
432 | 0 | out_paths[i] = new char[plugin_paths[i].size() + 1]; |
433 | 0 | strcpy(out_paths[i], plugin_paths[i].c_str()); |
434 | 0 | } |
435 | |
|
436 | 0 | out_paths[n] = nullptr; |
437 | |
|
438 | 0 | return out_paths; |
439 | 0 | } |
440 | | |
441 | | |
442 | | void heif_free_plugin_directories(const char* const* paths) |
443 | 0 | { |
444 | 0 | for (int i = 0; paths[i]; i++) { |
445 | 0 | delete[] paths[i]; |
446 | 0 | } |
447 | |
|
448 | 0 | delete[] paths; |
449 | 0 | } |