Program Listing for File bundled_gstreamer.cpp

Program Listing for File bundled_gstreamer.cpp#

↰ Return to documentation for file (Src/bundled_gstreamer.cpp)

// A Linux package carries the image's own GStreamer and plugins; see AGENTS.md.
#ifdef __linux__
#include "bundled_gstreamer.hpp"

#include <dlfcn.h>
#include <sys/stat.h>

#include <cstdlib>
#include <filesystem>
#include <functional>
#include <string>
#include <system_error>

namespace kataglyphis::bundled_gstreamer {

namespace {
    const char anchor = 0;
}

auto plugin_dir() -> std::string
{
    Dl_info info{};
    if (::dladdr(&anchor, &info) == 0 || info.dli_fname == nullptr) { return {}; }
    const std::string library = info.dli_fname;
    const std::size_t slash = library.find_last_of('/');
    if (slash == std::string::npos) { return {}; }
    // Beside the library when another package embeds it, under CPackOptions.cmake's PRIVATE_DIR in its own.
    for (const char *sub : { "/gstreamer-1.0", "/accelerantgine/gstreamer-1.0" }) {
        const std::string dir = library.substr(0, slash) + sub;
        struct stat status{};
        if (::stat(dir.c_str(), &status) != 0 || !S_ISDIR(status.st_mode)) { continue; }
        std::error_code error;
        const std::filesystem::path canonical = std::filesystem::canonical(dir, error);
        return error ? dir : canonical.string();
    }
    return {};
}

}// namespace kataglyphis::bundled_gstreamer

namespace {

void set_unless_set(const char *name, const std::string &value)
{
    if (::getenv(name) == nullptr) { ::setenv(name, value.c_str(), 1); }
}

auto registry_path(const std::string &plugins) -> std::string
{
    const char *cache = ::getenv("XDG_CACHE_HOME");
    const char *home = ::getenv("HOME");
    if ((cache == nullptr || *cache == '\0') && home == nullptr) { return {}; }
    const std::filesystem::path base =
      (cache != nullptr && *cache != '\0') ? std::filesystem::path(cache) : std::filesystem::path(home) / ".cache";
    // One registry per installed copy: a shared one is rescanned whenever another GStreamer used it last.
    const std::filesystem::path dir = base / "kataglyphis-gstreamer";
    std::error_code ignored;
    std::filesystem::create_directories(dir, ignored);
    return (dir / ("registry-" + std::to_string(std::hash<std::string>{}(plugins)) + ".bin")).string();
}

// Only the bundled plugins load: the distro's are built against another GStreamer, and the bundle ships no scanner.
__attribute__((constructor)) void use_bundled_gstreamer()
{
    const std::string plugins = kataglyphis::bundled_gstreamer::plugin_dir();
    if (plugins.empty()) { return; }
    set_unless_set("GST_PLUGIN_PATH", plugins);
    set_unless_set("GST_PLUGIN_SYSTEM_PATH", "");
    set_unless_set("GST_PLUGIN_SYSTEM_PATH_1_0", "");
    set_unless_set("GST_REGISTRY_FORK", "no");
    const std::string registry = registry_path(plugins);
    if (!registry.empty()) { set_unless_set("GST_REGISTRY", registry); }
}

}// namespace
#endif