X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Futil.cc;h=7a19790eb4ec261d1131ebd5e015565324f562a6;hb=931fa4ef2dbfb7c9f726c4dd41eea79621b49906;hp=96b834fcc88fe70d3f1be8429b25920a8322318f;hpb=f75cc4ebbffea7a7953af20e8a2ea124767bf949;p=dcpomatic.git diff --git a/src/lib/util.cc b/src/lib/util.cc index 96b834fcc..7a19790eb 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef DCPOMATIC_POSIX #include #include @@ -39,6 +40,9 @@ #include #include #include +#ifdef DCPOMATIC_WINDOWS +#include +#endif #include #include #include @@ -53,7 +57,6 @@ extern "C" { #include #include #include -#include #include } #include "util.h" @@ -66,6 +69,7 @@ extern "C" { #include "ratio.h" #include "job.h" #include "cross.h" +#include "video_content.h" #ifdef DCPOMATIC_WINDOWS #include "stack.hpp" #endif @@ -80,16 +84,19 @@ using std::endl; using std::vector; using std::hex; using std::setw; -using std::ifstream; using std::ios; using std::min; using std::max; using std::list; using std::multimap; +using std::map; using std::istream; using std::numeric_limits; using std::pair; -using std::ofstream; +using std::cout; +using std::bad_alloc; +using std::streampos; +using std::set_terminate; using boost::shared_ptr; using boost::thread; using boost::lexical_cast; @@ -240,7 +247,6 @@ dependency_version_summary () << N_("libavfilter ") << ffmpeg_version_to_string (avfilter_version()) << N_(", ") << N_("libavformat ") << ffmpeg_version_to_string (avformat_version()) << N_(", ") << N_("libavutil ") << ffmpeg_version_to_string (avutil_version()) << N_(", ") - << N_("libpostproc ") << ffmpeg_version_to_string (postproc_version()) << N_(", ") << N_("libswscale ") << ffmpeg_version_to_string (swscale_version()) << N_(", ") << MagickVersion << N_(", ") << N_("libssh ") << ssh_version (0) << N_(", ") @@ -259,12 +265,42 @@ seconds (struct timeval t) LONG WINAPI exception_handler(struct _EXCEPTION_POINTERS *) { dbg::stack s; - ofstream f (backtrace_file.string().c_str()); - std::copy(s.begin(), s.end(), std::ostream_iterator(f, "\n")); + FILE* f = fopen_boost (backtrace_file, "w"); + for (dbg::stack::const_iterator i = s.begin(); i != s.end(); ++i) { + fprintf (f, "%p %s %d %s", i->instruction, i->function.c_str(), i->line, i->module.c_str()); + } + fclose (f); return EXCEPTION_CONTINUE_SEARCH; } #endif +/* From http://stackoverflow.com/questions/2443135/how-do-i-find-where-an-exception-was-thrown-in-c */ +void +terminate () +{ + static bool tried_throw = false; + + try { + // try once to re-throw currently active exception + if (!tried_throw++) { + throw; + } + } + catch (const std::exception &e) { + std::cerr << __FUNCTION__ << " caught unhandled exception. what(): " + << e.what() << std::endl; + } + catch (...) { + std::cerr << __FUNCTION__ << " caught unknown/unhandled exception." + << std::endl; + } + +#ifdef DCPOMATIC_POSIX + stacktrace (cout, 50); +#endif + abort(); +} + /** 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. */ @@ -275,6 +311,21 @@ dcpomatic_setup () backtrace_file /= g_get_user_config_dir (); backtrace_file /= "backtrace.txt"; SetUnhandledExceptionFilter(exception_handler); + + /* Dark voodoo which, I think, gets boost::filesystem::path to + correctly convert UTF-8 strings to paths, and also paths + back to UTF-8 strings (on path::string()). + + After this, constructing boost::filesystem::paths from strings + converts from UTF-8 to UTF-16 inside the path. Then + path::string().c_str() gives UTF-8 and + path::c_str() gives UTF-16. + + This is all Windows-only. AFAICT Linux/OS X use UTF-8 everywhere, + so things are much simpler. + */ + std::locale::global (boost::locale::generator().generate ("")); + boost::filesystem::path::imbue (std::locale ()); #endif avfilter_register_all (); @@ -286,11 +337,14 @@ dcpomatic_setup () boost::filesystem::path lib = app_contents (); lib /= "lib"; setenv ("LTDL_LIBRARY_PATH", lib.c_str (), 1); -#endif +#endif + + set_terminate (terminate); libdcp::init (); Ratio::setup_ratios (); + VideoContentScale::setup_scales (); DCPContentType::setup_dcp_content_types (); Scaler::setup_scalers (); Filter::setup_filters (); @@ -329,6 +383,8 @@ dcpomatic_setup_gettext_i18n (string lang) putenv (cmd); snprintf (cmd, sizeof(cmd), "LANG=%s", lang.c_str ()); putenv (cmd); + snprintf (cmd, sizeof(cmd), "LC_ALL=%s", lang.c_str ()); + putenv (cmd); } setlocale (LC_ALL, ""); @@ -385,83 +441,42 @@ md5_digest (void const * data, int size) return s.str (); } -/** @param file File name. - * @return MD5 digest of file's contents. - */ -string -md5_digest (boost::filesystem::path file) -{ - ifstream f (file.string().c_str(), std::ios::binary); - if (!f.good ()) { - throw OpenFileError (file.string()); - } - - f.seekg (0, std::ios::end); - int bytes = f.tellg (); - f.seekg (0, std::ios::beg); - - int const buffer_size = 64 * 1024; - char buffer[buffer_size]; - - MD5_CTX md5_context; - MD5_Init (&md5_context); - while (bytes > 0) { - int const t = min (bytes, buffer_size); - f.read (buffer, t); - MD5_Update (&md5_context, buffer, t); - bytes -= t; - } - - 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 (); -} - /** @param job Optional job for which to report progress */ string -md5_digest_directory (boost::filesystem::path directory, shared_ptr job) +md5_digest (vector files, shared_ptr job) { - int const buffer_size = 64 * 1024; + boost::uintmax_t 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; - } + vector sizes; + for (size_t i = 0; i < files.size(); ++i) { + sizes.push_back (boost::filesystem::file_size (files[i])); } - 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()); + for (size_t i = 0; i < files.size(); ++i) { + FILE* f = fopen_boost (files[i], "rb"); + if (!f) { + throw OpenFileError (files[i].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); + boost::uintmax_t const bytes = boost::filesystem::file_size (files[i]); + boost::uintmax_t remaining = bytes; + + while (remaining > 0) { + int const t = min (remaining, buffer_size); + fread (buffer, 1, t, f); MD5_Update (&md5_context, buffer, t); - bytes -= t; - } + remaining -= t; - if (job) { - job->set_progress (float (j) / files); - ++j; + if (job) { + job->set_progress ((float (i) + 1 - float(remaining) / bytes) / files.size ()); + } } + + fclose (f); } unsigned char digest[MD5_DIGEST_LENGTH]; @@ -518,17 +533,27 @@ dcp_audio_frame_rate (int fs) 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); } @@ -539,7 +564,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; @@ -548,11 +573,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 (String::compose (_("error during async_connect (%1)"), ec.value ())); + } + + 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 (String::compose (_("error during async_accept (%1)"), ec.value ())); + } +} + /** Blocking write. * @param data Buffer to write. * @param size Number of bytes to write. @@ -570,7 +619,7 @@ Socket::write (uint8_t const * data, int size) } while (ec == boost::asio::error::would_block); if (ec) { - throw NetworkError (ec.message ()); + throw NetworkError (String::compose (_("error during async_write (%1)"), ec.value ())); } } @@ -598,7 +647,7 @@ Socket::read (uint8_t* data, int size) } while (ec == boost::asio::error::would_block); if (ec) { - throw NetworkError (ec.message ()); + throw NetworkError (String::compose (_("error during async_read (%1)"), ec.value ())); } } @@ -741,10 +790,11 @@ video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, fl string audio_channel_name (int c) { - assert (MAX_AUDIO_CHANNELS == 6); + assert (MAX_AUDIO_CHANNELS == 12); /* TRANSLATORS: these are the names of audio channels; Lfe (sub) is the low-frequency - enhancement channel (sub-woofer). + enhancement channel (sub-woofer). HI is the hearing-impaired audio track and + VI is the visually-impaired audio track (audio describe). */ string const channels[] = { _("Left"), @@ -753,6 +803,12 @@ audio_channel_name (int c) _("Lfe (sub)"), _("Left surround"), _("Right surround"), + _("Hearing impaired"), + _("Visually impaired"), + _("Left centre"), + _("Right centre"), + _("Left rear surround"), + _("Right rear surround"), }; return channels[c]; @@ -891,12 +947,121 @@ make_signer () return shared_ptr (new libdcp::Signer (chain, signer_key)); } +map +split_get_request (string url) +{ + enum { + AWAITING_QUESTION_MARK, + KEY, + VALUE + } state = AWAITING_QUESTION_MARK; + + map r; + string k; + string v; + for (size_t i = 0; i < url.length(); ++i) { + switch (state) { + case AWAITING_QUESTION_MARK: + if (url[i] == '?') { + state = KEY; + } + break; + case KEY: + if (url[i] == '=') { + v.clear (); + state = VALUE; + } else { + k += url[i]; + } + break; + case VALUE: + if (url[i] == '&') { + r.insert (make_pair (k, v)); + k.clear (); + state = KEY; + } else { + v += url[i]; + } + break; + } + } + + if (state == VALUE) { + r.insert (make_pair (k, v)); + } + + return r; +} + libdcp::Size fit_ratio_within (float ratio, libdcp::Size full_frame) { if (ratio < full_frame.ratio ()) { - return libdcp::Size (full_frame.height * ratio, full_frame.height); + return libdcp::Size (rint (full_frame.height * ratio), full_frame.height); } - return libdcp::Size (full_frame.width, full_frame.width / ratio); + return libdcp::Size (full_frame.width, rint (full_frame.width / ratio)); +} + +void * +wrapped_av_malloc (size_t s) +{ + void* p = av_malloc (s); + if (!p) { + throw bad_alloc (); + } + return p; +} + +string +entities_to_text (string e) +{ + boost::algorithm::replace_all (e, "%3A", ":"); + boost::algorithm::replace_all (e, "%2F", "/"); + return e; +} + +int64_t +divide_with_round (int64_t a, int64_t b) +{ + if (a % b >= (b / 2)) { + return (a + b - 1) / b; + } else { + return a / b; + } +} + +ScopedTemporary::ScopedTemporary () + : _open (0) +{ + _file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path (); +} + +ScopedTemporary::~ScopedTemporary () +{ + close (); + boost::system::error_code ec; + boost::filesystem::remove (_file, ec); +} + +char const * +ScopedTemporary::c_str () const +{ + return _file.string().c_str (); +} + +FILE* +ScopedTemporary::open (char const * params) +{ + _open = fopen (c_str(), params); + return _open; +} + +void +ScopedTemporary::close () +{ + if (_open) { + fclose (_open); + _open = 0; + } }