libheif/init.cc (279 lines of code) (raw):

/* * HEIF codec. * Copyright (c) 2022 Dirk Farin <dirk.farin@gmail.com> * * This file is part of libheif. * * libheif is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * libheif is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libheif. If not, see <http://www.gnu.org/licenses/>. */ #include "init.h" #include "libheif/heif.h" #include "error.h" #include "plugin_registry.h" #include "common_utils.h" #include "color-conversion/colorconversion.h" #if ENABLE_MULTITHREADING_SUPPORT #include <mutex> #endif #if defined(_WIN32) #include "plugins_windows.h" #else #include "plugins_unix.h" #endif void heif_unload_all_plugins(); #if ENABLE_PLUGIN_LOADING void heif_unregister_encoder_plugin(const heif_encoder_plugin* plugin); std::vector<std::string> get_plugin_paths() { std::vector<std::string> plugin_paths; #if defined(_WIN32) plugin_paths = get_plugin_directories_from_environment_variable_windows(); #else plugin_paths = get_plugin_directories_from_environment_variable_unix(); #endif if (plugin_paths.empty()) { plugin_paths.push_back(LIBHEIF_PLUGIN_DIRECTORY); } return plugin_paths; } std::vector<std::string> list_all_potential_plugins_in_directory(const char* directory) { #if defined(_WIN32) return list_all_potential_plugins_in_directory_windows(directory); #else return list_all_potential_plugins_in_directory_unix(directory); #endif } #else std::vector<std::string> get_plugin_paths() { return {}; } #endif static int heif_library_initialization_count = 0; static bool default_plugins_registered = true; // because they are implicitly registered at startup #if ENABLE_MULTITHREADING_SUPPORT static std::recursive_mutex& heif_init_mutex() { static std::recursive_mutex init_mutex; return init_mutex; } #endif void load_plugins_if_not_initialized_yet() { if (heif_library_initialization_count == 0) { heif_init(nullptr); } } struct heif_error heif_init(struct heif_init_params*) { #if ENABLE_MULTITHREADING_SUPPORT std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); #endif if (heif_library_initialization_count == 0) { ColorConversionPipeline::init_ops(); // --- initialize builtin plugins if (!default_plugins_registered) { register_default_plugins(); } #if ENABLE_PLUGIN_LOADING struct heif_error err{}; std::vector<std::string> plugin_paths = get_plugin_paths(); for (const auto& dir : plugin_paths) { err = heif_load_plugins(dir.c_str(), nullptr, nullptr, 0); if (err.code != 0) { return err; } } #endif } // Note: it is important that we increase the counter AFTER initialization such that 'load_plugins_if_not_initialized_yet()' can check this // without having to lock the mutex. heif_library_initialization_count++; return {heif_error_Ok, heif_suberror_Unspecified, Error::kSuccess}; } void heif_deinit() { #if ENABLE_MULTITHREADING_SUPPORT std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); #endif if (heif_library_initialization_count == 0) { // This case should never happen (heif_deinit() is called more often then heif_init()). return; } if (heif_library_initialization_count == 1) { heif_unregister_decoder_plugins(); heif_unregister_encoder_plugins(); default_plugins_registered = false; heif_unload_all_plugins(); ColorConversionPipeline::release_ops(); } // Note: contrary to heif_init() I think it does not matter whether we decrease the counter before or after deinitialization. // If the client application calls heif_deinit() in parallel to some other libheif function, it is really broken. heif_library_initialization_count--; } // This could be inside ENABLE_PLUGIN_LOADING, but the "include-what-you-use" checker cannot process this. #include <vector> #include <string> #include <cstring> #if ENABLE_PLUGIN_LOADING #if defined(_WIN32) typedef PluginLibrary_Windows PluginLibrary_SysDep; #else typedef PluginLibrary_Unix PluginLibrary_SysDep; #endif struct loaded_plugin { PluginLibrary_SysDep plugin_library_handle; struct heif_plugin_info* info = nullptr; int openCnt = 0; }; static std::vector<loaded_plugin> sLoadedPlugins; MAYBE_UNUSED heif_error error_dlopen{heif_error_Plugin_loading_error, heif_suberror_Plugin_loading_error, "Cannot open plugin (dlopen)."}; 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."}; MAYBE_UNUSED heif_error error_cannot_read_plugin_directory{heif_error_Plugin_loading_error, heif_suberror_Cannot_read_plugin_directory, "Cannot read plugin directory."}; MAYBE_UNUSED static void unregister_plugin(const heif_plugin_info* info) { switch (info->type) { case heif_plugin_type_encoder: { auto* encoder_plugin = static_cast<const heif_encoder_plugin*>(info->plugin); heif_unregister_encoder_plugin(encoder_plugin); break; } case heif_plugin_type_decoder: { // TODO } } } struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin) { #if ENABLE_MULTITHREADING_SUPPORT std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); #endif PluginLibrary_SysDep plugin; auto err = plugin.load_from_file(filename); if (err.code) { return err; } heif_plugin_info* plugin_info = plugin.get_plugin_info(); // --- check whether the plugin is already loaded // If yes, return pointer to existing plugin. for (auto& p : sLoadedPlugins) { if (p.plugin_library_handle == plugin) { if (out_plugin) { *out_plugin = p.info; p.openCnt++; return heif_error_ok; } } } loaded_plugin loadedPlugin; loadedPlugin.plugin_library_handle = plugin; loadedPlugin.openCnt = 1; loadedPlugin.info = plugin_info; sLoadedPlugins.push_back(loadedPlugin); *out_plugin = plugin_info; switch (loadedPlugin.info->type) { case heif_plugin_type_encoder: { auto* encoder_plugin = static_cast<const heif_encoder_plugin*>(plugin_info->plugin); struct heif_error err = heif_register_encoder_plugin(encoder_plugin); if (err.code) { return err; } break; } case heif_plugin_type_decoder: { auto* decoder_plugin = static_cast<const heif_decoder_plugin*>(plugin_info->plugin); struct heif_error err = heif_register_decoder_plugin(decoder_plugin); if (err.code) { return err; } break; } } return heif_error_ok; } struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin) { #if ENABLE_MULTITHREADING_SUPPORT std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); #endif for (size_t i = 0; i < sLoadedPlugins.size(); i++) { auto& p = sLoadedPlugins[i]; if (p.info == plugin) { p.plugin_library_handle.release(); p.openCnt--; if (p.openCnt == 0) { unregister_plugin(plugin); sLoadedPlugins[i] = sLoadedPlugins.back(); sLoadedPlugins.pop_back(); } return heif_error_ok; } } return error_plugin_not_loaded; } void heif_unload_all_plugins() { #if ENABLE_MULTITHREADING_SUPPORT std::lock_guard<std::recursive_mutex> lock(heif_init_mutex()); #endif for (auto& p : sLoadedPlugins) { unregister_plugin(p.info); for (int i = 0; i < p.openCnt; i++) { p.plugin_library_handle.release(); } } sLoadedPlugins.clear(); } struct heif_error heif_load_plugins(const char* directory, const struct heif_plugin_info** out_plugins, int* out_nPluginsLoaded, int output_array_size) { auto libraryFiles = list_all_potential_plugins_in_directory(directory); int nPlugins = 0; for (const auto& filename : libraryFiles) { const struct heif_plugin_info* info = nullptr; auto err = heif_load_plugin(filename.c_str(), &info); if (err.code == 0) { if (out_plugins) { if (nPlugins == output_array_size) { break; } out_plugins[nPlugins] = info; } nPlugins++; } } if (nPlugins < output_array_size && out_plugins) { out_plugins[nPlugins] = nullptr; } if (out_nPluginsLoaded) { *out_nPluginsLoaded = nPlugins; } return heif_error_ok; } #else static heif_error heif_error_plugins_unsupported{heif_error_Unsupported_feature, heif_suberror_Unspecified, "Plugins are not supported"}; struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin) { return heif_error_plugins_unsupported; } struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin) { return heif_error_plugins_unsupported; } void heif_unload_all_plugins() {} struct heif_error heif_load_plugins(const char* directory, const struct heif_plugin_info** out_plugins, int* out_nPluginsLoaded, int output_array_size) { if (out_nPluginsLoaded) { *out_nPluginsLoaded = 0; } return heif_error_plugins_unsupported; } #endif const char* const* heif_get_plugin_directories() { auto plugin_paths = get_plugin_paths(); size_t n = plugin_paths.size(); auto out_paths = new char* [n + 1]; for (size_t i = 0; i < n; i++) { out_paths[i] = new char[plugin_paths[i].size() + 1]; strcpy(out_paths[i], plugin_paths[i].c_str()); } out_paths[n] = nullptr; return out_paths; } void heif_free_plugin_directories(const char* const* paths) { for (int i = 0; paths[i]; i++) { delete[] paths[i]; } delete[] paths; }