/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 | | void heif_unload_all_plugins(); |
43 | | |
44 | | #if ENABLE_PLUGIN_LOADING |
45 | | |
46 | | void heif_unregister_encoder_plugin(const heif_encoder_plugin* plugin); |
47 | | |
48 | | std::vector<std::string> get_plugin_paths() |
49 | | { |
50 | | std::vector<std::string> plugin_paths; |
51 | | |
52 | | #if defined(_WIN32) |
53 | | plugin_paths = get_plugin_directories_from_environment_variable_windows(); |
54 | | #else |
55 | | plugin_paths = get_plugin_directories_from_environment_variable_unix(); |
56 | | #endif |
57 | | |
58 | | if (plugin_paths.empty()) { |
59 | | plugin_paths.push_back(LIBHEIF_PLUGIN_DIRECTORY); |
60 | | } |
61 | | |
62 | | return plugin_paths; |
63 | | } |
64 | | |
65 | | std::vector<std::string> list_all_potential_plugins_in_directory(const char* directory) |
66 | | { |
67 | | #if defined(_WIN32) |
68 | | return list_all_potential_plugins_in_directory_windows(directory); |
69 | | #else |
70 | | return list_all_potential_plugins_in_directory_unix(directory); |
71 | | #endif |
72 | | } |
73 | | |
74 | | #else |
75 | | std::vector<std::string> get_plugin_paths() |
76 | 0 | { |
77 | 0 | return {}; |
78 | 0 | } |
79 | | #endif |
80 | | |
81 | | |
82 | | static int heif_library_initialization_count = 0; |
83 | | static bool default_plugins_registered = true; // because they are implicitly registered at startup |
84 | | |
85 | | |
86 | | #if ENABLE_MULTITHREADING_SUPPORT |
87 | | |
88 | | static std::recursive_mutex& heif_init_mutex() |
89 | 532 | { |
90 | 532 | static std::recursive_mutex init_mutex; |
91 | 532 | return init_mutex; |
92 | 532 | } |
93 | | |
94 | | #endif |
95 | | |
96 | | |
97 | | void load_plugins_if_not_initialized_yet() |
98 | 1.16k | { |
99 | 1.16k | if (heif_library_initialization_count == 0) { |
100 | 2 | heif_init(nullptr); |
101 | 2 | } |
102 | 1.16k | } |
103 | | |
104 | | |
105 | | heif_error heif_init(heif_init_params*) |
106 | 267 | { |
107 | 267 | #if ENABLE_MULTITHREADING_SUPPORT |
108 | 267 | std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); |
109 | 267 | #endif |
110 | | |
111 | 267 | if (heif_library_initialization_count == 0) { |
112 | | |
113 | 2 | ColorConversionPipeline::init_ops(); |
114 | | |
115 | | // --- initialize builtin plugins |
116 | | |
117 | 2 | if (!default_plugins_registered) { |
118 | 0 | register_default_plugins(); |
119 | 0 | } |
120 | | |
121 | | #if ENABLE_PLUGIN_LOADING |
122 | | struct heif_error err{}; |
123 | | std::vector<std::string> plugin_paths = get_plugin_paths(); |
124 | | |
125 | | for (const auto& dir : plugin_paths) { |
126 | | err = heif_load_plugins(dir.c_str(), nullptr, nullptr, 0); |
127 | | if (err.code != 0) { |
128 | | return err; |
129 | | } |
130 | | } |
131 | | #endif |
132 | 2 | } |
133 | | |
134 | | // Note: it is important that we increase the counter AFTER initialization such that |
135 | | // 'load_plugins_if_not_initialized_yet()' can check this without having to lock the mutex. |
136 | 267 | heif_library_initialization_count++; |
137 | | |
138 | 267 | return {heif_error_Ok, heif_suberror_Unspecified, Error::kSuccess}; |
139 | 267 | } |
140 | | |
141 | | |
142 | | void heif_deinit() |
143 | 265 | { |
144 | 265 | #if ENABLE_MULTITHREADING_SUPPORT |
145 | 265 | std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); |
146 | 265 | #endif |
147 | | |
148 | 265 | if (heif_library_initialization_count == 0) { |
149 | | // This case should never happen (heif_deinit() is called more often than heif_init()). |
150 | 0 | return; |
151 | 0 | } |
152 | | |
153 | 265 | if (heif_library_initialization_count == 1) { |
154 | 0 | heif_unregister_decoder_plugins(); |
155 | 0 | heif_unregister_encoder_plugins(); |
156 | 0 | default_plugins_registered = false; |
157 | |
|
158 | 0 | heif_unload_all_plugins(); |
159 | |
|
160 | 0 | ColorConversionPipeline::release_ops(); |
161 | 0 | } |
162 | | |
163 | | // Note: contrary to heif_init() I think it does not matter whether we decrease the counter before or after deinitialization. |
164 | | // If the client application calls heif_deinit() in parallel to some other libheif function, it is really broken. |
165 | 265 | heif_library_initialization_count--; |
166 | 265 | } |
167 | | |
168 | | |
169 | | // This could be inside ENABLE_PLUGIN_LOADING, but the "include-what-you-use" checker cannot process this. |
170 | | #include <vector> |
171 | | #include <string> |
172 | | #include <cstring> |
173 | | |
174 | | #if ENABLE_PLUGIN_LOADING |
175 | | |
176 | | #if defined(_WIN32) |
177 | | typedef PluginLibrary_Windows PluginLibrary_SysDep; |
178 | | #else |
179 | | typedef PluginLibrary_Unix PluginLibrary_SysDep; |
180 | | #endif |
181 | | |
182 | | |
183 | | struct loaded_plugin |
184 | | { |
185 | | PluginLibrary_SysDep plugin_library_handle; |
186 | | struct heif_plugin_info* info = nullptr; |
187 | | int openCnt = 0; |
188 | | }; |
189 | | |
190 | | static std::vector<loaded_plugin> sLoadedPlugins; |
191 | | |
192 | | MAYBE_UNUSED heif_error error_dlopen{heif_error_Plugin_loading_error, heif_suberror_Plugin_loading_error, "Cannot open plugin (dlopen)."}; |
193 | | 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."}; |
194 | | MAYBE_UNUSED heif_error error_cannot_read_plugin_directory{heif_error_Plugin_loading_error, heif_suberror_Cannot_read_plugin_directory, "Cannot read plugin directory."}; |
195 | | |
196 | | MAYBE_UNUSED static void unregister_plugin(const heif_plugin_info* info) |
197 | | { |
198 | | switch (info->type) { |
199 | | case heif_plugin_type_encoder: { |
200 | | auto* encoder_plugin = static_cast<const heif_encoder_plugin*>(info->plugin); |
201 | | heif_unregister_encoder_plugin(encoder_plugin); |
202 | | break; |
203 | | } |
204 | | case heif_plugin_type_decoder: { |
205 | | // TODO |
206 | | } |
207 | | } |
208 | | } |
209 | | |
210 | | |
211 | | struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin) |
212 | | { |
213 | | #if ENABLE_MULTITHREADING_SUPPORT |
214 | | std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); |
215 | | #endif |
216 | | |
217 | | PluginLibrary_SysDep plugin; |
218 | | auto err = plugin.load_from_file(filename); |
219 | | if (err.code) { |
220 | | return err; |
221 | | } |
222 | | |
223 | | heif_plugin_info* plugin_info = plugin.get_plugin_info(); |
224 | | |
225 | | // --- check whether the plugin is already loaded |
226 | | // If yes, return pointer to existing plugin. |
227 | | |
228 | | for (auto& p : sLoadedPlugins) { |
229 | | if (p.plugin_library_handle == plugin) { |
230 | | if (out_plugin) { |
231 | | *out_plugin = p.info; |
232 | | p.openCnt++; |
233 | | return heif_error_ok; |
234 | | } |
235 | | } |
236 | | } |
237 | | |
238 | | loaded_plugin loadedPlugin; |
239 | | loadedPlugin.plugin_library_handle = plugin; |
240 | | loadedPlugin.openCnt = 1; |
241 | | loadedPlugin.info = plugin_info; |
242 | | sLoadedPlugins.push_back(loadedPlugin); |
243 | | |
244 | | *out_plugin = plugin_info; |
245 | | |
246 | | switch (loadedPlugin.info->type) { |
247 | | case heif_plugin_type_encoder: { |
248 | | auto* encoder_plugin = static_cast<const heif_encoder_plugin*>(plugin_info->plugin); |
249 | | struct heif_error err = heif_register_encoder_plugin(encoder_plugin); |
250 | | if (err.code) { |
251 | | return err; |
252 | | } |
253 | | break; |
254 | | } |
255 | | |
256 | | case heif_plugin_type_decoder: { |
257 | | auto* decoder_plugin = static_cast<const heif_decoder_plugin*>(plugin_info->plugin); |
258 | | struct heif_error err = heif_register_decoder_plugin(decoder_plugin); |
259 | | if (err.code) { |
260 | | return err; |
261 | | } |
262 | | break; |
263 | | } |
264 | | } |
265 | | |
266 | | return heif_error_ok; |
267 | | } |
268 | | |
269 | | struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin) |
270 | | { |
271 | | #if ENABLE_MULTITHREADING_SUPPORT |
272 | | std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); |
273 | | #endif |
274 | | |
275 | | for (size_t i = 0; i < sLoadedPlugins.size(); i++) { |
276 | | auto& p = sLoadedPlugins[i]; |
277 | | |
278 | | if (p.info == plugin) { |
279 | | p.plugin_library_handle.release(); |
280 | | p.openCnt--; |
281 | | |
282 | | if (p.openCnt == 0) { |
283 | | unregister_plugin(plugin); |
284 | | |
285 | | sLoadedPlugins[i] = sLoadedPlugins.back(); |
286 | | sLoadedPlugins.pop_back(); |
287 | | } |
288 | | |
289 | | return heif_error_ok; |
290 | | } |
291 | | } |
292 | | |
293 | | return error_plugin_not_loaded; |
294 | | } |
295 | | |
296 | | void heif_unload_all_plugins() |
297 | | { |
298 | | #if ENABLE_MULTITHREADING_SUPPORT |
299 | | std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); |
300 | | #endif |
301 | | |
302 | | for (auto& p : sLoadedPlugins) { |
303 | | unregister_plugin(p.info); |
304 | | |
305 | | for (int i = 0; i < p.openCnt; i++) { |
306 | | p.plugin_library_handle.release(); |
307 | | } |
308 | | } |
309 | | |
310 | | sLoadedPlugins.clear(); |
311 | | } |
312 | | |
313 | | |
314 | | struct heif_error heif_load_plugins(const char* directory, |
315 | | const struct heif_plugin_info** out_plugins, |
316 | | int* out_nPluginsLoaded, |
317 | | int output_array_size) |
318 | | { |
319 | | auto libraryFiles = list_all_potential_plugins_in_directory(directory); |
320 | | |
321 | | int nPlugins = 0; |
322 | | |
323 | | for (const auto& filename : libraryFiles) { |
324 | | const struct heif_plugin_info* info = nullptr; |
325 | | auto err = heif_load_plugin(filename.c_str(), &info); |
326 | | if (err.code == 0) { |
327 | | if (out_plugins) { |
328 | | if (nPlugins == output_array_size) { |
329 | | break; |
330 | | } |
331 | | |
332 | | out_plugins[nPlugins] = info; |
333 | | } |
334 | | |
335 | | nPlugins++; |
336 | | } |
337 | | } |
338 | | |
339 | | if (nPlugins < output_array_size && out_plugins) { |
340 | | out_plugins[nPlugins] = nullptr; |
341 | | } |
342 | | |
343 | | if (out_nPluginsLoaded) { |
344 | | *out_nPluginsLoaded = nPlugins; |
345 | | } |
346 | | |
347 | | return heif_error_ok; |
348 | | } |
349 | | |
350 | | #else |
351 | | static heif_error heif_error_plugins_unsupported{heif_error_Unsupported_feature, heif_suberror_Unspecified, "Plugins are not supported"}; |
352 | | |
353 | | struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin) |
354 | 0 | { |
355 | 0 | return heif_error_plugins_unsupported; |
356 | 0 | } |
357 | | |
358 | | |
359 | | struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin) |
360 | 0 | { |
361 | 0 | return heif_error_plugins_unsupported; |
362 | 0 | } |
363 | | |
364 | | |
365 | 0 | void heif_unload_all_plugins() {} |
366 | | |
367 | | struct heif_error heif_load_plugins(const char* directory, |
368 | | const struct heif_plugin_info** out_plugins, |
369 | | int* out_nPluginsLoaded, |
370 | | int output_array_size) |
371 | 0 | { |
372 | 0 | if (out_nPluginsLoaded) { |
373 | 0 | *out_nPluginsLoaded = 0; |
374 | 0 | } |
375 | |
|
376 | 0 | return heif_error_plugins_unsupported; |
377 | 0 | } |
378 | | |
379 | | #endif |
380 | | |
381 | | |
382 | | const char* const* heif_get_plugin_directories() |
383 | 0 | { |
384 | 0 | auto plugin_paths = get_plugin_paths(); |
385 | 0 | size_t n = plugin_paths.size(); |
386 | |
|
387 | 0 | auto out_paths = new char* [n + 1]; |
388 | 0 | for (size_t i = 0; i < n; i++) { |
389 | 0 | out_paths[i] = new char[plugin_paths[i].size() + 1]; |
390 | 0 | strcpy(out_paths[i], plugin_paths[i].c_str()); |
391 | 0 | } |
392 | |
|
393 | 0 | out_paths[n] = nullptr; |
394 | |
|
395 | 0 | return out_paths; |
396 | 0 | } |
397 | | |
398 | | |
399 | | void heif_free_plugin_directories(const char* const* paths) |
400 | 0 | { |
401 | 0 | for (int i = 0; paths[i]; i++) { |
402 | 0 | delete[] paths[i]; |
403 | 0 | } |
404 | |
|
405 | 0 | delete[] paths; |
406 | 0 | } |