Add minimal player HTTP server (#2830).
[dcpomatic.git] / src / lib / util.cc
index 8c570059821d35ca3290dd250524d3f682b2c6f9..172b8d763e73dee9a959e0b0cd402a7e2f227f2d 100644 (file)
@@ -33,7 +33,6 @@
 #include "config.h"
 #include "constants.h"
 #include "cross.h"
-#include "crypto.h"
 #include "dcp_content_type.h"
 #include "dcpomatic_log.h"
 #include "digester.h"
 #include "ratio.h"
 #include "rect.h"
 #include "render_text.h"
-#include "scope_guard.h"
 #include "string_text.h"
 #include "text_decoder.h"
 #include "util.h"
+#include "variant.h"
 #include "video_content.h"
 #include <dcp/atmos_asset.h>
 #include <dcp/decrypted_kdm.h>
+#include <dcp/file.h>
+#include <dcp/filesystem.h>
 #include <dcp/locale_convert.h>
+#include <dcp/mpeg2_picture_asset.h>
 #include <dcp/picture_asset.h>
 #include <dcp/raw_convert.h>
+#include <dcp/scope_guard.h>
 #include <dcp/sound_asset.h>
 #include <dcp/subtitle_asset.h>
 #include <dcp/util.h>
@@ -86,10 +89,11 @@ LIBDCP_ENABLE_WARNINGS
 #include <dbghelp.h>
 #endif
 #include <signal.h>
+#include <climits>
 #include <iomanip>
 #include <iostream>
 #include <fstream>
-#include <climits>
+#include <numeric>
 #include <stdexcept>
 #ifdef DCPOMATIC_POSIX
 #include <execinfo.h>
@@ -101,6 +105,7 @@ LIBDCP_ENABLE_WARNINGS
 
 using std::bad_alloc;
 using std::cout;
+using std::dynamic_pointer_cast;
 using std::endl;
 using std::istream;
 using std::list;
@@ -117,9 +122,6 @@ using std::vector;
 using std::wstring;
 using boost::thread;
 using boost::optional;
-using boost::lexical_cast;
-using boost::bad_lexical_cast;
-using boost::scoped_array;
 using dcp::Size;
 using dcp::raw_convert;
 using dcp::locale_convert;
@@ -242,6 +244,7 @@ addr2line (void const * const addr)
 {
        char addr2line_cmd[512] = { 0 };
        sprintf (addr2line_cmd, "addr2line -f -p -e %.256s %p > %s", program_name.c_str(), addr, backtrace_file.string().c_str());
+       std::cout << addr2line_cmd << "\n";
        return system(addr2line_cmd);
 }
 
@@ -407,7 +410,6 @@ ffmpeg_log_callback(void* ptr, int level, const char* fmt, va_list vl)
 }
 
 
-static
 void
 capture_ffmpeg_logs()
 {
@@ -428,6 +430,11 @@ dcpomatic_setup ()
        SetUnhandledExceptionFilter(exception_handler);
 #endif
 
+#ifdef DCPOMATIC_GROK
+       /* This makes grok support work with CUDA 12.2 */
+       setenv("CUDA_MODULE_LOADING", "EAGER", 1);
+#endif
+
 #ifdef DCPOMATIC_HAVE_AVREGISTER
 LIBDCP_DISABLE_WARNINGS
        av_register_all ();
@@ -448,7 +455,13 @@ LIBDCP_ENABLE_WARNINGS
 
 #ifdef DCPOMATIC_WINDOWS
        putenv ("PANGOCAIRO_BACKEND=fontconfig");
-       putenv (String::compose("FONTCONFIG_PATH=%1", resources_path().string()).c_str());
+       if (dcp::filesystem::exists(resources_path() / "fonts.conf")) {
+               /* The actual application after installation */
+               putenv(String::compose("FONTCONFIG_PATH=%1", resources_path().string()).c_str());
+       } else {
+               /* The place where fonts.conf is during tests */
+               putenv("FONTCONFIG_PATH=build\\fonts");
+       }
 #endif
 
 #ifdef DCPOMATIC_OSX
@@ -461,10 +474,10 @@ LIBDCP_ENABLE_WARNINGS
 
 #if defined(DCPOMATIC_WINDOWS) || defined(DCPOMATIC_OSX)
        /* Render something to fontconfig to create its cache */
-       list<StringText> subs;
+       vector<StringText> subs;
        dcp::SubtitleString ss(
                optional<string>(), false, false, false, dcp::Colour(), 42, 1, dcp::Time(), dcp::Time(), 0, dcp::HAlign::CENTER, 0, dcp::VAlign::CENTER, 0, dcp::Direction::LTR,
-               "Hello dolly", dcp::Effect::NONE, dcp::Colour(), dcp::Time(), dcp::Time(), 0
+               "Hello dolly", dcp::Effect::NONE, dcp::Colour(), dcp::Time(), dcp::Time(), 0, std::vector<dcp::Ruby>()
                );
        subs.push_back(StringText(ss, 0, make_shared<dcpomatic::Font>("foo"), dcp::SubtitleStandard::SMPTE_2014));
        render_text (subs, dcp::Size(640, 480), DCPTime(), 24);
@@ -503,7 +516,7 @@ mo_path ()
 boost::filesystem::path
 mo_path ()
 {
-       return "DCP-o-matic 2.app/Contents/Resources";
+       return variant::dcpomatic_app() + "/Contents/Resources";
 }
 #endif
 
@@ -556,7 +569,7 @@ digest_head_tail (vector<boost::filesystem::path> files, boost::uintmax_t size)
                        throw OpenFileError (files[i].string(), errno, OpenFileError::READ);
                }
 
-               boost::uintmax_t this_time = min (to_do, boost::filesystem::file_size (files[i]));
+               auto this_time = min(to_do, dcp::filesystem::file_size(files[i]));
                f.checked_read(p, this_time);
                p += this_time;
                to_do -= this_time;
@@ -575,7 +588,7 @@ digest_head_tail (vector<boost::filesystem::path> files, boost::uintmax_t size)
                        throw OpenFileError (files[i].string(), errno, OpenFileError::READ);
                }
 
-               boost::uintmax_t this_time = min (to_do, boost::filesystem::file_size (files[i]));
+               auto this_time = min(to_do, dcp::filesystem::file_size(files[i]));
                f.seek(-this_time, SEEK_END);
                f.checked_read(p, this_time);
                p += this_time;
@@ -592,7 +605,8 @@ digest_head_tail (vector<boost::filesystem::path> files, boost::uintmax_t size)
 string
 simple_digest (vector<boost::filesystem::path> paths)
 {
-       return digest_head_tail(paths, 1000000) + raw_convert<string>(boost::filesystem::file_size(paths.front()));
+       DCP_ASSERT(!paths.empty());
+       return digest_head_tail(paths, 1000000) + raw_convert<string>(dcp::filesystem::file_size(paths.front()));
 }
 
 
@@ -667,7 +681,7 @@ short_audio_channel_name (int c)
 bool
 valid_image_file (boost::filesystem::path f)
 {
-       if (boost::starts_with (f.leaf().string(), "._")) {
+       if (boost::starts_with(f.filename().string(), "._")) {
                return false;
        }
 
@@ -684,7 +698,7 @@ valid_image_file (boost::filesystem::path f)
 bool
 valid_sound_file (boost::filesystem::path f)
 {
-       if (boost::starts_with (f.leaf().string(), "._")) {
+       if (boost::starts_with(f.filename().string(), "._")) {
                return false;
        }
 
@@ -727,16 +741,17 @@ asset_filename (shared_ptr<dcp::Asset> asset, string type, int reel_index, int r
        values['r'] = raw_convert<string>(reel_index + 1);
        values['n'] = raw_convert<string>(reel_count);
        if (summary) {
-               values['c'] = careful_string_filter(summary.get());
+               values['c'] = summary.get();
        }
-       return Config::instance()->dcp_asset_filename_format().get(values, "_" + asset->id() + extension);
+       return careful_string_filter(Config::instance()->dcp_asset_filename_format().get(values, "_" + asset->id() + extension));
 }
 
 
 string
-video_asset_filename (shared_ptr<dcp::PictureAsset> asset, int reel_index, int reel_count, optional<string> summary)
+video_asset_filename(shared_ptr<dcp::PictureAsset> asset, int reel_index, int reel_count, optional<string> summary)
 {
-       return asset_filename(asset, "j2c", reel_index, reel_count, summary, ".mxf");
+       string type = dynamic_pointer_cast<dcp::MPEG2PictureAsset>(asset) ? "mpeg2" : "j2c";
+       return asset_filename(asset, type, reel_index, reel_count, summary, ".mxf");
 }
 
 
@@ -793,7 +808,7 @@ careful_string_filter (string s)
 
        /* Then remove anything that's not in a very limited character set */
        wstring out;
-       wstring const allowed = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_%.+";
+       wstring const allowed = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.+";
        for (int i = 0; i < transliterated_more.length(); ++i) {
                wchar_t c = transliterated_more[i];
                if (allowed.find(c) != string::npos) {
@@ -833,6 +848,8 @@ audio_channel_types (list<int> mapped, int channels)
                case dcp::Channel::BSR:
                        ++non_lfe;
                        break;
+               case dcp::Channel::LC:
+               case dcp::Channel::RC:
                case dcp::Channel::HI:
                case dcp::Channel::VI:
                case dcp::Channel::MOTION_DATA:
@@ -870,16 +887,6 @@ remap (shared_ptr<const AudioBuffers> input, int output_channels, AudioMapping m
        return mapped;
 }
 
-Eyes
-increment_eyes (Eyes e)
-{
-       if (e == Eyes::LEFT) {
-               return Eyes::RIGHT;
-       }
-
-       return Eyes::LEFT;
-}
-
 
 size_t
 utf8_strlen (string s)
@@ -959,7 +966,7 @@ copy_in_bits (boost::filesystem::path from, boost::filesystem::path to, std::fun
 
        std::vector<uint8_t> buffer(chunk);
 
-       boost::uintmax_t const total = boost::filesystem::file_size (from);
+       auto const total = dcp::filesystem::file_size(from);
        boost::uintmax_t remaining = total;
 
        while (remaining) {
@@ -1015,9 +1022,9 @@ decrypt_kdm_with_helpful_error (dcp::EncryptedKDM kdm)
                        }
                }
                if (!on_chain) {
-                       throw KDMError (_("This KDM was not made for DCP-o-matic's decryption certificate."), e.what());
+                       throw KDMError(variant::insert_dcpomatic(_("This KDM was not made for %1's decryption certificate.")), e.what());
                } else if (kdm_subject_name != dc->leaf().subject()) {
-                       throw KDMError (_("This KDM was made for DCP-o-matic but not for its leaf certificate."), e.what());
+                       throw KDMError(variant::insert_dcpomatic(_("This KDM was made for %1 but not for its leaf certificate.")), e.what());
                } else {
                        throw;
                }
@@ -1031,7 +1038,7 @@ default_font_file ()
        boost::filesystem::path liberation_normal;
        try {
                liberation_normal = resources_path() / "LiberationSans-Regular.ttf";
-               if (!boost::filesystem::exists (liberation_normal)) {
+               if (!dcp::filesystem::exists(liberation_normal)) {
                        /* Hack for unit tests */
                        liberation_normal = resources_path() / "fonts" / "LiberationSans-Regular.ttf";
                }
@@ -1039,10 +1046,10 @@ default_font_file ()
 
        }
 
-       if (!boost::filesystem::exists(liberation_normal)) {
+       if (!dcp::filesystem::exists(liberation_normal)) {
                liberation_normal = "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf";
        }
-       if (!boost::filesystem::exists(liberation_normal)) {
+       if (!dcp::filesystem::exists(liberation_normal)) {
                liberation_normal = "/usr/share/fonts/liberation-sans/LiberationSans-Regular.ttf";
        }
 
@@ -1078,7 +1085,7 @@ error_details(boost::system::error_code ec)
 bool
 contains_assetmap(boost::filesystem::path dir)
 {
-       return boost::filesystem::is_regular_file(dir / "ASSETMAP") || boost::filesystem::is_regular_file(dir / "ASSETMAP.xml");
+       return dcp::filesystem::is_regular_file(dir / "ASSETMAP") || dcp::filesystem::is_regular_file(dir / "ASSETMAP.xml");
 }
 
 
@@ -1088,7 +1095,7 @@ word_wrap(string input, int columns)
        icu::Locale locale;
        UErrorCode status = U_ZERO_ERROR;
        auto iter = icu::BreakIterator::createLineInstance(locale, status);
-       ScopeGuard sg = [iter]() { delete iter; };
+       dcp::ScopeGuard sg = [iter]() { delete iter; };
        if (U_FAILURE(status)) {
                return input;
        }
@@ -1116,3 +1123,79 @@ word_wrap(string input, int columns)
        return output;
 }
 
+
+#ifdef DCPOMATIC_GROK
+void
+setup_grok_library_path()
+{
+       static std::string old_path;
+       if (old_path.empty()) {
+               auto const old = getenv("LD_LIRARY_PATH");
+               if (old) {
+                       old_path = old;
+               }
+       }
+       auto const grok = Config::instance()->grok();
+       if (!grok || grok->binary_location.empty()) {
+               setenv("LD_LIRARY_PATH", old_path.c_str(), 1);
+               return;
+       }
+
+       std::string new_path = old_path;
+       if (!new_path.empty()) {
+               new_path += ":";
+       }
+       new_path += grok->binary_location.string();
+
+       setenv("LD_LIBRARY_PATH", new_path.c_str(), 1);
+}
+#endif
+
+
+string
+screen_names_to_string(vector<string> names)
+{
+       if (names.empty()) {
+               return {};
+       }
+
+       auto number = [](string const& s) {
+               return s.find_first_not_of("0123456789") == string::npos;
+       };
+
+       if (std::find_if(names.begin(), names.end(), [number](string const& s) { return !number(s); }) == names.end()) {
+               std::sort(names.begin(), names.end(), [](string const& a, string const& b) {
+                       return dcp::raw_convert<int>(a) < dcp::raw_convert<int>(b);
+               });
+       } else {
+               std::sort(names.begin(), names.end());
+       }
+
+       string result;
+       for (auto const& name: names) {
+               result += name + ", ";
+       }
+
+       return result.substr(0, result.length() - 2);
+}
+
+
+string
+report_problem()
+{
+       return String::compose(_("Please report this problem by using Help -> Report a problem or via email to %1"), variant::report_problem_email());
+}
+
+
+string
+join_strings(vector<string> const& in, string const& separator)
+{
+       if (in.empty()) {
+               return {};
+       }
+
+       return std::accumulate(std::next(in.begin()), in.end(), in.front(), [separator](string a, string b) {
+               return a + separator + b;
+       });
+}
+