X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Futil.cc;h=48b418d37634fe6ecdd41bb507ae6d54830485d5;hb=3753cb8685e1755b067676345a5871db24149d0f;hp=e2ce94dd9ea8f75eaf84a4841afe17298a99c211;hpb=f660fe3f1be97f373318806a77b3ce3fcd53cb73;p=dcpomatic.git diff --git a/src/lib/util.cc b/src/lib/util.cc index e2ce94dd9..48b418d37 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 @@ -66,6 +70,7 @@ extern "C" { #include "ratio.h" #include "job.h" #include "cross.h" +#include "video_content.h" #ifdef DCPOMATIC_WINDOWS #include "stack.hpp" #endif @@ -80,17 +85,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; @@ -260,12 +267,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. */ @@ -276,6 +313,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 (); @@ -287,11 +339,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 (); @@ -330,6 +385,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, ""); @@ -386,83 +443,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]; @@ -560,7 +576,7 @@ Socket::connect (boost::asio::ip::tcp::endpoint endpoint) } while (ec == boost::asio::error::would_block); if (ec) { - throw NetworkError (ec.message ()); + throw NetworkError (String::compose (_("error during async_connect (%1)"), ec.value ())); } if (!_socket.is_open ()) { @@ -584,7 +600,7 @@ Socket::accept (int port) _acceptor = 0; if (ec) { - throw NetworkError (ec.message ()); + throw NetworkError (String::compose (_("error during async_accept (%1)"), ec.value ())); } } @@ -605,7 +621,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 ())); } } @@ -633,7 +649,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 ())); } } @@ -776,10 +792,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 == 8); /* 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"), @@ -788,6 +805,8 @@ audio_channel_name (int c) _("Lfe (sub)"), _("Left surround"), _("Right surround"), + _("HI"), + _("VI") }; return channels[c]; @@ -926,12 +945,86 @@ 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; + } }