X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Futil.cc;h=484c4fb9ba012edb895b0c466f484af2028bd2d8;hb=a0c0e609f7fbff8513135428378cc16684bae937;hp=71a21105b6ec2f6e9eb38096fbbf0ce36bb2dcba;hpb=a0856e3fbef17f24073b01cb96be6bbcb229ecbc;p=dcpomatic.git diff --git a/src/lib/util.cc b/src/lib/util.cc index 71a21105b..484c4fb9b 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -45,6 +45,9 @@ #include #include #include +#include +#include +#include extern "C" { #include #include @@ -61,7 +64,9 @@ extern "C" { #include "sound_processor.h" #include "config.h" #include "ratio.h" -#ifdef DVDOMATIC_WINDOWS +#include "job.h" +#include "cross.h" +#ifdef DCPOMATIC_WINDOWS #include "stack.hpp" #endif @@ -85,14 +90,15 @@ using std::istream; using std::numeric_limits; using std::pair; using std::ofstream; +using std::cout; using boost::shared_ptr; using boost::thread; using boost::lexical_cast; using boost::optional; using libdcp::Size; -boost::thread::id ui_thread; -boost::filesystem::path backtrace_file; +static boost::thread::id ui_thread; +static boost::filesystem::path backtrace_file; /** Convert some number of seconds to a string representation * in hours, minutes and seconds. @@ -119,12 +125,6 @@ seconds_to_hms (int s) return hms.str (); } -string -time_to_hms (Time t) -{ - return seconds_to_hms (t / TIME_HZ); -} - /** @param s Number of seconds. * @return String containing an approximate description of s (e.g. "about 2 hours") */ @@ -207,15 +207,11 @@ void stacktrace (ostream& out, int levels) { void *array[200]; - size_t size; - char **strings; - size_t i; - - size = backtrace (array, 200); - strings = backtrace_symbols (array, size); + size_t size = backtrace (array, 200); + char** strings = backtrace_symbols (array, size); if (strings) { - for (i = 0; i < size && (levels == 0 || i < size_t(levels)); i++) { + for (size_t i = 0; i < size && (levels == 0 || i < size_t(levels)); i++) { out << N_(" ") << demangle (strings[i]) << "\n"; } @@ -260,7 +256,7 @@ seconds (struct timeval t) return t.tv_sec + (double (t.tv_usec) / 1e6); } -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS LONG WINAPI exception_handler(struct _EXCEPTION_POINTERS *) { dbg::stack s; @@ -270,19 +266,30 @@ LONG WINAPI exception_handler(struct _EXCEPTION_POINTERS *) } #endif -/** Call the required functions to set up DVD-o-matic's static arrays, etc. +/** Call the required functions to set up DCP-o-matic's static arrays, etc. * Must be called from the UI thread, if there is one. */ void dcpomatic_setup () { -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS backtrace_file /= g_get_user_config_dir (); backtrace_file /= "backtrace.txt"; SetUnhandledExceptionFilter(exception_handler); #endif avfilter_register_all (); + +#ifdef DCPOMATIC_OSX + /* Add our lib directory to the libltdl search path so that + xmlsec can find xmlsec1-openssl. + */ + boost::filesystem::path lib = app_contents (); + lib /= "lib"; + setenv ("LTDL_LIBRARY_PATH", lib.c_str (), 1); +#endif + + libdcp::init (); Ratio::setup_ratios (); DCPContentType::setup_dcp_content_types (); @@ -338,18 +345,6 @@ dcpomatic_setup_gettext_i18n (string lang) #endif } -/** @param start Start position for the crop within the image. - * @param size Size of the cropped area. - * @return FFmpeg crop filter string. - */ -string -crop_string (Position start, libdcp::Size size) -{ - stringstream s; - s << N_("crop=") << size.width << N_(":") << size.height << N_(":") << start.x << N_(":") << start.y; - return s.str (); -} - /** @param s A string. * @return Parts of the string split at spaces, except when a space is within quotation marks. */ @@ -429,6 +424,58 @@ md5_digest (boost::filesystem::path file) return s.str (); } +/** @param job Optional job for which to report progress */ +string +md5_digest_directory (boost::filesystem::path directory, shared_ptr job) +{ + int const buffer_size = 64 * 1024; + char buffer[buffer_size]; + + MD5_CTX md5_context; + MD5_Init (&md5_context); + + int files = 0; + if (job) { + for (boost::filesystem::directory_iterator i(directory); i != boost::filesystem::directory_iterator(); ++i) { + ++files; + } + } + + int j = 0; + for (boost::filesystem::directory_iterator i(directory); i != boost::filesystem::directory_iterator(); ++i) { + ifstream f (i->path().string().c_str(), std::ios::binary); + if (!f.good ()) { + throw OpenFileError (i->path().string()); + } + + f.seekg (0, std::ios::end); + int bytes = f.tellg (); + f.seekg (0, std::ios::beg); + + while (bytes > 0) { + int const t = min (bytes, buffer_size); + f.read (buffer, t); + MD5_Update (&md5_context, buffer, t); + bytes -= t; + } + + if (job) { + job->set_progress (float (j) / files); + ++j; + } + } + + unsigned char digest[MD5_DIGEST_LENGTH]; + MD5_Final (digest, &md5_context); + + stringstream s; + for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) { + s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]); + } + + return s.str (); +} + static bool about_equal (float a, float b) { @@ -469,37 +516,30 @@ dcp_audio_frame_rate (int fs) return 96000; } -/** @param index Colour LUT index. - * @return Human-readable name. - */ -string -colour_lut_index_to_name (int index) -{ - switch (index) { - case 0: - return _("sRGB"); - case 1: - return _("Rec 709"); - } - - assert (false); - return N_(""); -} - Socket::Socket (int timeout) : _deadline (_io_service) , _socket (_io_service) + , _acceptor (0) , _timeout (timeout) { _deadline.expires_at (boost::posix_time::pos_infin); check (); } +Socket::~Socket () +{ + delete _acceptor; +} + void Socket::check () { if (_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now ()) { - _socket.close (); + if (_acceptor) { + _acceptor->cancel (); + } else { + _socket.close (); + } _deadline.expires_at (boost::posix_time::pos_infin); } @@ -510,7 +550,7 @@ Socket::check () * @param endpoint End-point to connect to. */ void -Socket::connect (boost::asio::ip::basic_resolver_entry const & endpoint) +Socket::connect (boost::asio::ip::tcp::endpoint endpoint) { _deadline.expires_from_now (boost::posix_time::seconds (_timeout)); boost::system::error_code ec = boost::asio::error::would_block; @@ -519,11 +559,35 @@ Socket::connect (boost::asio::ip::basic_resolver_entry con _io_service.run_one(); } while (ec == boost::asio::error::would_block); - if (ec || !_socket.is_open ()) { + if (ec) { + throw NetworkError (ec.message ()); + } + + if (!_socket.is_open ()) { throw NetworkError (_("connect timed out")); } } +void +Socket::accept (int port) +{ + _acceptor = new boost::asio::ip::tcp::acceptor (_io_service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)); + + _deadline.expires_from_now (boost::posix_time::seconds (_timeout)); + boost::system::error_code ec = boost::asio::error::would_block; + _acceptor->async_accept (_socket, boost::lambda::var(ec) = boost::lambda::_1); + do { + _io_service.run_one (); + } while (ec == boost::asio::error::would_block ); + + delete _acceptor; + _acceptor = 0; + + if (ec) { + throw NetworkError (ec.message ()); + } +} + /** Blocking write. * @param data Buffer to write. * @param size Number of bytes to write. @@ -594,12 +658,6 @@ stride_round_up (int c, int const * stride, int t) return a - (a % t); } -int -stride_lookup (int c, int const * stride) -{ - return stride[c]; -} - /** Read a sequence of key / value pairs from a text stream; * the keys are the first words on the line, and the values are * the remainder of the line following the key. Lines beginning @@ -710,44 +768,18 @@ ensure_ui_thread () * @return Equivalent number of audio frames for `v'. */ int64_t -video_frames_to_audio_frames (ContentVideoFrame v, float audio_sample_rate, float frames_per_second) +video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second) { return ((int64_t) v * audio_sample_rate / frames_per_second); } -/** @return A pair containing CPU model name and the number of processors */ -pair -cpu_info () -{ - pair info; - info.second = 0; - -#ifdef DCPOMATIC_POSIX - ifstream f (N_("/proc/cpuinfo")); - while (f.good ()) { - string l; - getline (f, l); - if (boost::algorithm::starts_with (l, N_("model name"))) { - string::size_type const c = l.find (':'); - if (c != string::npos) { - info.first = l.substr (c + 2); - } - } else if (boost::algorithm::starts_with (l, N_("processor"))) { - ++info.second; - } - } -#endif - - return info; -} - string audio_channel_name (int c) { assert (MAX_AUDIO_CHANNELS == 6); /* TRANSLATORS: these are the names of audio channels; Lfe (sub) is the low-frequency - enhancement channel (sub-woofer)./ + enhancement channel (sub-woofer). */ string const channels[] = { _("Left"), @@ -763,29 +795,38 @@ audio_channel_name (int c) FrameRateConversion::FrameRateConversion (float source, int dcp) : skip (false) - , repeat (false) + , repeat (1) , change_speed (false) { - if (fabs (source / 2.0 - dcp) < (fabs (source - dcp))) { + if (fabs (source / 2.0 - dcp) < fabs (source - dcp)) { + /* The difference between source and DCP frame rate will be lower + (i.e. better) if we skip. + */ skip = true; } else if (fabs (source * 2 - dcp) < fabs (source - dcp)) { - repeat = true; + /* The difference between source and DCP frame rate would be better + if we repeated each frame once; it may be better still if we + repeated more than once. Work out the required repeat. + */ + repeat = round (dcp / source); } change_speed = !about_equal (source * factor(), dcp); - if (!skip && !repeat && !change_speed) { - description = _("DCP and source have the same rate.\n"); + if (!skip && repeat == 1 && !change_speed) { + description = _("Content and DCP have the same rate.\n"); } else { if (skip) { - description = _("DCP will use every other frame of the source.\n"); - } else if (repeat) { - description = _("Each source frame will be doubled in the DCP.\n"); + description = _("DCP will use every other frame of the content.\n"); + } else if (repeat == 2) { + description = _("Each content frame will be doubled in the DCP.\n"); + } else if (repeat > 2) { + description = String::compose (_("Each content frame will be repeated %1 more times in the DCP.\n"), repeat - 1); } if (change_speed) { float const pc = dcp * 100 / (source * factor()); - description += String::compose (_("DCP will run at %1%% of the source speed.\n"), pc); + description += String::compose (_("DCP will run at %1%% of the content speed.\n"), pc); } } } @@ -795,12 +836,12 @@ LocaleGuard::LocaleGuard () { char const * old = setlocale (LC_NUMERIC, 0); - if (old) { - _old = strdup (old); - if (strcmp (_old, "C")) { - setlocale (LC_NUMERIC, "C"); - } - } + if (old) { + _old = strdup (old); + if (strcmp (_old, "C")) { + setlocale (LC_NUMERIC, "C"); + } + } } LocaleGuard::~LocaleGuard () @@ -808,3 +849,89 @@ LocaleGuard::~LocaleGuard () setlocale (LC_NUMERIC, _old); free (_old); } + +bool +valid_image_file (boost::filesystem::path f) +{ + string ext = f.extension().string(); + transform (ext.begin(), ext.end(), ext.begin(), ::tolower); + return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp" || ext == ".tga"); +} + +string +tidy_for_filename (string f) +{ + string t; + for (size_t i = 0; i < f.length(); ++i) { + if (isalnum (f[i]) || f[i] == '_' || f[i] == '-') { + t += f[i]; + } else { + t += '_'; + } + } + + return t; +} + +shared_ptr +make_signer () +{ + boost::filesystem::path const sd = Config::instance()->signer_chain_directory (); + + /* Remake the chain if any of it is missing */ + + list files; + files.push_back ("ca.self-signed.pem"); + files.push_back ("intermediate.signed.pem"); + files.push_back ("leaf.signed.pem"); + files.push_back ("leaf.key"); + + list::const_iterator i = files.begin(); + while (i != files.end()) { + boost::filesystem::path p (sd); + p /= *i; + if (!boost::filesystem::exists (p)) { + boost::filesystem::remove_all (sd); + boost::filesystem::create_directories (sd); + libdcp::make_signer_chain (sd, openssl_path ()); + break; + } + + ++i; + } + + libdcp::CertificateChain chain; + + { + boost::filesystem::path p (sd); + p /= "ca.self-signed.pem"; + chain.add (shared_ptr (new libdcp::Certificate (p))); + } + + { + boost::filesystem::path p (sd); + p /= "intermediate.signed.pem"; + chain.add (shared_ptr (new libdcp::Certificate (p))); + } + + { + boost::filesystem::path p (sd); + p /= "leaf.signed.pem"; + chain.add (shared_ptr (new libdcp::Certificate (p))); + } + + boost::filesystem::path signer_key (sd); + signer_key /= "leaf.key"; + + return shared_ptr (new libdcp::Signer (chain, signer_key)); +} + +libdcp::Size +fit_ratio_within (float ratio, libdcp::Size full_frame) +{ + if (ratio < full_frame.ratio ()) { + return libdcp::Size (rint (full_frame.height * ratio), full_frame.height); + } + + return libdcp::Size (full_frame.width, rint (full_frame.width / ratio)); +}