+
+/** XXX: could use mmap? */
+void
+copy_in_bits (boost::filesystem::path from, boost::filesystem::path to, std::function<void (float)> progress)
+{
+ dcp::File f(from, "rb");
+ if (!f) {
+ throw OpenFileError (from, errno, OpenFileError::READ);
+ }
+ dcp::File t(to, "wb");
+ if (!t) {
+ throw OpenFileError (to, errno, OpenFileError::WRITE);
+ }
+
+ /* on the order of a second's worth of copying */
+ boost::uintmax_t const chunk = 20 * 1024 * 1024;
+
+ std::vector<uint8_t> buffer(chunk);
+
+ boost::uintmax_t const total = boost::filesystem::file_size (from);
+ boost::uintmax_t remaining = total;
+
+ while (remaining) {
+ boost::uintmax_t this_time = min (chunk, remaining);
+ size_t N = f.read(buffer.data(), 1, chunk);
+ if (N < this_time) {
+ throw ReadFileError (from, errno);
+ }
+
+ N = t.write(buffer.data(), 1, this_time);
+ if (N < this_time) {
+ throw WriteFileError (to, errno);
+ }
+
+ progress (1 - float(remaining) / total);
+ remaining -= this_time;
+ }
+}
+
+
+dcp::Size
+scale_for_display (dcp::Size s, dcp::Size display_container, dcp::Size film_container, PixelQuanta quanta)
+{
+ /* Now scale it down if the display container is smaller than the film container */
+ if (display_container != film_container) {
+ float const scale = min (
+ float (display_container.width) / film_container.width,
+ float (display_container.height) / film_container.height
+ );
+
+ s.width = lrintf (s.width * scale);
+ s.height = lrintf (s.height * scale);
+ s = quanta.round (s);
+ }
+
+ return s;
+}
+
+
+dcp::DecryptedKDM
+decrypt_kdm_with_helpful_error (dcp::EncryptedKDM kdm)
+{
+ try {
+ return dcp::DecryptedKDM (kdm, Config::instance()->decryption_chain()->key().get());
+ } catch (dcp::KDMDecryptionError& e) {
+ /* Try to flesh out the error a bit */
+ auto const kdm_subject_name = kdm.recipient_x509_subject_name();
+ bool on_chain = false;
+ auto dc = Config::instance()->decryption_chain();
+ for (auto i: dc->root_to_leaf()) {
+ if (i.subject() == kdm_subject_name) {
+ on_chain = true;
+ }
+ }
+ if (!on_chain) {
+ throw KDMError (_("This KDM was not made for DCP-o-matic'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());
+ } else {
+ throw;
+ }
+ }
+}
+
+
+boost::filesystem::path
+default_font_file ()
+{
+ boost::filesystem::path liberation_normal;
+ try {
+ liberation_normal = resources_path() / "LiberationSans-Regular.ttf";
+ if (!boost::filesystem::exists (liberation_normal)) {
+ /* Hack for unit tests */
+ liberation_normal = resources_path() / "fonts" / "LiberationSans-Regular.ttf";
+ }
+ } catch (boost::filesystem::filesystem_error& e) {
+
+ }
+
+ if (!boost::filesystem::exists(liberation_normal)) {
+ liberation_normal = "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf";
+ }
+ if (!boost::filesystem::exists(liberation_normal)) {
+ liberation_normal = "/usr/share/fonts/liberation-sans/LiberationSans-Regular.ttf";
+ }
+
+ return liberation_normal;
+}
+
+
+string
+to_upper (string s)
+{
+ transform (s.begin(), s.end(), s.begin(), ::toupper);
+ return s;
+}
+
+
+/* Set to 1 to print the IDs of some of our threads to stdout on creation */
+#define DCPOMATIC_DEBUG_THREADS 0
+
+#if DCPOMATIC_DEBUG_THREADS
+void
+start_of_thread (string name)
+{
+ std::cout << "THREAD:" << name << ":" << std::hex << pthread_self() << "\n";
+}
+#else
+void
+start_of_thread (string)
+{
+
+}
+#endif
+
+
+class LogSink : public Kumu::ILogSink
+{
+public:
+ LogSink () {}
+ LogSink (LogSink const&) = delete;
+ LogSink& operator= (LogSink const&) = delete;
+
+ void WriteEntry(const Kumu::LogEntry& entry) override {
+ Kumu::AutoMutex L(m_lock);
+ WriteEntryToListeners(entry);
+ if (entry.TestFilter(m_filter)) {
+ string buffer;
+ entry.CreateStringWithOptions(buffer, m_options);
+ LOG_GENERAL("asdcplib: %1", buffer);
+ }
+ }
+};
+
+
+void
+capture_asdcp_logs ()
+{
+ static LogSink log_sink;
+ Kumu::SetDefaultLogSink(&log_sink);
+}
+