X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Futil.cc;h=eda0d0236767300f349c146660d29a1f24a24595;hb=a183c1776cfd020a37d028ebb0f641352f49697b;hp=557e9a34bd26563406292c7dce8cbfde55794426;hpb=588ddc8856b5188c5c666280da1b2c962e64dedb;p=dcpomatic.git diff --git a/src/lib/util.cc b/src/lib/util.cc index 557e9a34b..eda0d0236 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -27,7 +27,7 @@ #include #include #include -#ifdef DVDOMATIC_POSIX +#ifdef DCPOMATIC_POSIX #include #include #endif @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -55,19 +56,43 @@ extern "C" { #include "util.h" #include "exceptions.h" #include "scaler.h" -#include "format.h" #include "dcp_content_type.h" #include "filter.h" #include "sound_processor.h" #include "config.h" +#include "ratio.h" +#ifdef DCPOMATIC_WINDOWS +#include "stack.hpp" +#endif #include "i18n.h" -using namespace std; -using namespace boost; +using std::string; +using std::stringstream; +using std::setfill; +using std::ostream; +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::istream; +using std::numeric_limits; +using std::pair; +using std::ofstream; +using boost::shared_ptr; +using boost::thread; +using boost::lexical_cast; +using boost::optional; using libdcp::Size; -thread::id ui_thread; +boost::thread::id ui_thread; +boost::filesystem::path backtrace_file; /** Convert some number of seconds to a string representation * in hours, minutes and seconds. @@ -87,13 +112,19 @@ seconds_to_hms (int s) stringstream hms; hms << h << N_(":"); hms.width (2); - hms << setfill ('0') << m << N_(":"); + hms << std::setfill ('0') << m << N_(":"); hms.width (2); - hms << setfill ('0') << s; + hms << std::setfill ('0') << 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") */ @@ -130,7 +161,7 @@ seconds_to_approximate_hms (int s) return ap.str (); } -#ifdef DVDOMATIC_POSIX +#ifdef DCPOMATIC_POSIX /** @param l Mangled C++ identifier. * @return Demangled version. */ @@ -185,7 +216,7 @@ stacktrace (ostream& out, int levels) if (strings) { for (i = 0; i < size && (levels == 0 || i < size_t(levels)); i++) { - out << N_(" ") << demangle (strings[i]) << endl; + out << N_(" ") << demangle (strings[i]) << "\n"; } free (strings); @@ -229,24 +260,40 @@ seconds (struct timeval t) return t.tv_sec + (double (t.tv_usec) / 1e6); } +#ifdef DCPOMATIC_WINDOWS +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")); + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + /** Call the required functions to set up DVD-o-matic's static arrays, etc. * Must be called from the UI thread, if there is one. */ void -dvdomatic_setup () +dcpomatic_setup () { +#ifdef DCPOMATIC_WINDOWS + backtrace_file /= g_get_user_config_dir (); + backtrace_file /= "backtrace.txt"; + SetUnhandledExceptionFilter(exception_handler); +#endif + avfilter_register_all (); - Format::setup_formats (); + Ratio::setup_ratios (); DCPContentType::setup_dcp_content_types (); Scaler::setup_scalers (); Filter::setup_filters (); SoundProcessor::setup_sound_processors (); - ui_thread = this_thread::get_id (); + ui_thread = boost::this_thread::get_id (); } -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS boost::filesystem::path mo_path () { @@ -261,9 +308,9 @@ mo_path () #endif void -dvdomatic_setup_i18n (string lang) +dcpomatic_setup_gettext_i18n (string lang) { -#ifdef DVDOMATIC_POSIX +#ifdef DCPOMATIC_POSIX lang += ".UTF8"; #endif @@ -279,15 +326,15 @@ dvdomatic_setup_i18n (string lang) } setlocale (LC_ALL, ""); - textdomain ("libdvdomatic"); + textdomain ("libdcpomatic"); -#ifdef DVDOMATIC_WINDOWS - bindtextdomain ("libdvdomatic", mo_path().string().c_str()); - bind_textdomain_codeset ("libdvdomatic", "UTF8"); +#ifdef DCPOMATIC_WINDOWS + bindtextdomain ("libdcpomatic", mo_path().string().c_str()); + bind_textdomain_codeset ("libdcpomatic", "UTF8"); #endif -#ifdef DVDOMATIC_POSIX - bindtextdomain ("libdvdomatic", POSIX_LOCALE_PREFIX); +#ifdef DCPOMATIC_POSIX + bindtextdomain ("libdcpomatic", POSIX_LOCALE_PREFIX); #endif } @@ -338,7 +385,7 @@ md5_digest (void const * data, int size) stringstream s; for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) { - s << hex << setfill('0') << setw(2) << ((int) digest[i]); + s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]); } return s.str (); @@ -348,16 +395,16 @@ md5_digest (void const * data, int size) * @return MD5 digest of file's contents. */ string -md5_digest (string file) +md5_digest (boost::filesystem::path file) { - ifstream f (file.c_str(), ios::binary); + ifstream f (file.string().c_str(), std::ios::binary); if (!f.good ()) { - throw OpenFileError (file); + throw OpenFileError (file.string()); } - f.seekg (0, ios::end); + f.seekg (0, std::ios::end); int bytes = f.tellg (); - f.seekg (0, ios::beg); + f.seekg (0, std::ios::beg); int const buffer_size = 64 * 1024; char buffer[buffer_size]; @@ -376,7 +423,7 @@ md5_digest (string file) stringstream s; for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) { - s << hex << setfill('0') << setw(2) << ((int) digest[i]); + s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]); } return s.str (); @@ -409,66 +456,11 @@ about_equal (float a, float b) return (fabs (a - b) < 1e-4); } -class FrameRateCandidate -{ -public: - FrameRateCandidate (float source_, int dcp_) - : source (source_) - , dcp (dcp_) - {} - - float source; - int dcp; -}; - -int -best_dcp_frame_rate (float source_fps) -{ - list const allowed_dcp_frame_rates = Config::instance()->allowed_dcp_frame_rates (); - - /* Work out what rates we could manage, including those achieved by using skip / repeat. */ - list candidates; - - /* Start with the ones without skip / repeat so they will get matched in preference to skipped/repeated ones */ - for (list::const_iterator i = allowed_dcp_frame_rates.begin(); i != allowed_dcp_frame_rates.end(); ++i) { - candidates.push_back (FrameRateCandidate (*i, *i)); - } - - /* Then the skip/repeat ones */ - for (list::const_iterator i = allowed_dcp_frame_rates.begin(); i != allowed_dcp_frame_rates.end(); ++i) { - candidates.push_back (FrameRateCandidate (float (*i) / 2, *i)); - candidates.push_back (FrameRateCandidate (float (*i) * 2, *i)); - } - - /* Pick the best one, bailing early if we hit an exact match */ - float error = numeric_limits::max (); - boost::optional best; - list::iterator i = candidates.begin(); - while (i != candidates.end()) { - - if (about_equal (i->source, source_fps)) { - best = *i; - break; - } - - float const e = fabs (i->source - source_fps); - if (e < error) { - error = e; - best = *i; - } - - ++i; - } - - assert (best); - return best->dcp; -} - -/** @param An arbitrary sampling rate. - * @return The appropriate DCP-approved sampling rate (48kHz or 96kHz). +/** @param An arbitrary audio frame rate. + * @return The appropriate DCP-approved frame rate (48kHz or 96kHz). */ int -dcp_audio_sample_rate (int fs) +dcp_audio_frame_rate (int fs) { if (fs <= 48000) { return 48000; @@ -477,16 +469,6 @@ dcp_audio_sample_rate (int fs) return 96000; } -bool operator== (Crop const & a, Crop const & b) -{ - return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom); -} - -bool operator!= (Crop const & a, Crop const & b) -{ - return !(a == b); -} - /** @param index Colour LUT index. * @return Human-readable name. */ @@ -509,16 +491,16 @@ Socket::Socket (int timeout) , _socket (_io_service) , _timeout (timeout) { - _deadline.expires_at (posix_time::pos_infin); + _deadline.expires_at (boost::posix_time::pos_infin); check (); } void Socket::check () { - if (_deadline.expires_at() <= asio::deadline_timer::traits_type::now ()) { + if (_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now ()) { _socket.close (); - _deadline.expires_at (posix_time::pos_infin); + _deadline.expires_at (boost::posix_time::pos_infin); } _deadline.async_wait (boost::bind (&Socket::check, this)); @@ -528,14 +510,14 @@ Socket::check () * @param endpoint End-point to connect to. */ void -Socket::connect (asio::ip::basic_resolver_entry const & endpoint) +Socket::connect (boost::asio::ip::basic_resolver_entry const & endpoint) { - _deadline.expires_from_now (posix_time::seconds (_timeout)); - system::error_code ec = asio::error::would_block; - _socket.async_connect (endpoint, lambda::var(ec) = lambda::_1); + _deadline.expires_from_now (boost::posix_time::seconds (_timeout)); + boost::system::error_code ec = boost::asio::error::would_block; + _socket.async_connect (endpoint, boost::lambda::var(ec) = boost::lambda::_1); do { _io_service.run_one(); - } while (ec == asio::error::would_block); + } while (ec == boost::asio::error::would_block); if (ec || !_socket.is_open ()) { throw NetworkError (_("connect timed out")); @@ -549,14 +531,14 @@ Socket::connect (asio::ip::basic_resolver_entry const & endpoint) void Socket::write (uint8_t const * data, int size) { - _deadline.expires_from_now (posix_time::seconds (_timeout)); - system::error_code ec = asio::error::would_block; + _deadline.expires_from_now (boost::posix_time::seconds (_timeout)); + boost::system::error_code ec = boost::asio::error::would_block; - asio::async_write (_socket, asio::buffer (data, size), lambda::var(ec) = lambda::_1); + boost::asio::async_write (_socket, boost::asio::buffer (data, size), boost::lambda::var(ec) = boost::lambda::_1); do { _io_service.run_one (); - } while (ec == asio::error::would_block); + } while (ec == boost::asio::error::would_block); if (ec) { throw NetworkError (ec.message ()); @@ -577,14 +559,14 @@ Socket::write (uint32_t v) void Socket::read (uint8_t* data, int size) { - _deadline.expires_from_now (posix_time::seconds (_timeout)); - system::error_code ec = asio::error::would_block; + _deadline.expires_from_now (boost::posix_time::seconds (_timeout)); + boost::system::error_code ec = boost::asio::error::would_block; - asio::async_read (_socket, asio::buffer (data, size), lambda::var(ec) = lambda::_1); + boost::asio::async_read (_socket, boost::asio::buffer (data, size), boost::lambda::var(ec) = boost::lambda::_1); do { _io_service.run_one (); - } while (ec == asio::error::would_block); + } while (ec == boost::asio::error::would_block); if (ec) { throw NetworkError (ec.message ()); @@ -599,22 +581,6 @@ Socket::read_uint32 () return ntohl (v); } -/** @param other A Rect. - * @return The intersection of this with `other'. - */ -Rect -Rect::intersection (Rect const & other) const -{ - int const tx = max (x, other.x); - int const ty = max (y, other.y); - - return Rect ( - tx, ty, - min (x + width, other.x + other.width) - tx, - min (y + height, other.y + other.height) - ty - ); -} - /** Round a number up to the nearest multiple of another number. * @param c Index. * @param s Array of numbers to round, indexed by c. @@ -731,193 +697,24 @@ get_optional_int (multimap const & kv, string k) return lexical_cast (i->second); } -/** Construct an AudioBuffers. Audio data is undefined after this constructor. - * @param channels Number of channels. - * @param frames Number of frames to reserve space for. - */ -AudioBuffers::AudioBuffers (int channels, int frames) - : _channels (channels) - , _frames (frames) - , _allocated_frames (frames) -{ - _data = new float*[_channels]; - for (int i = 0; i < _channels; ++i) { - _data[i] = new float[frames]; - } -} - -/** Copy constructor. - * @param other Other AudioBuffers; data is copied. - */ -AudioBuffers::AudioBuffers (AudioBuffers const & other) - : _channels (other._channels) - , _frames (other._frames) - , _allocated_frames (other._frames) -{ - _data = new float*[_channels]; - for (int i = 0; i < _channels; ++i) { - _data[i] = new float[_frames]; - memcpy (_data[i], other._data[i], _frames * sizeof (float)); - } -} - -/** AudioBuffers destructor */ -AudioBuffers::~AudioBuffers () -{ - for (int i = 0; i < _channels; ++i) { - delete[] _data[i]; - } - - delete[] _data; -} - -/** @param c Channel index. - * @return Buffer for this channel. - */ -float* -AudioBuffers::data (int c) const -{ - assert (c >= 0 && c < _channels); - return _data[c]; -} - -/** Set the number of frames that these AudioBuffers will report themselves - * as having. - * @param f Frames; must be less than or equal to the number of allocated frames. - */ -void -AudioBuffers::set_frames (int f) -{ - assert (f <= _allocated_frames); - _frames = f; -} - -/** Make all samples on all channels silent */ -void -AudioBuffers::make_silent () -{ - for (int i = 0; i < _channels; ++i) { - make_silent (i); - } -} - -/** Make all samples on a given channel silent. - * @param c Channel. - */ -void -AudioBuffers::make_silent (int c) -{ - assert (c >= 0 && c < _channels); - - for (int i = 0; i < _frames; ++i) { - _data[c][i] = 0; - } -} - -/** Copy data from another AudioBuffers to this one. All channels are copied. - * @param from AudioBuffers to copy from; must have the same number of channels as this. - * @param frames_to_copy Number of frames to copy. - * @param read_offset Offset to read from in `from'. - * @param write_offset Offset to write to in `to'. - */ -void -AudioBuffers::copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset) -{ - assert (from->channels() == channels()); - - assert (from); - assert (read_offset >= 0 && (read_offset + frames_to_copy) <= from->_allocated_frames); - assert (write_offset >= 0 && (write_offset + frames_to_copy) <= _allocated_frames); - - for (int i = 0; i < _channels; ++i) { - memcpy (_data[i] + write_offset, from->_data[i] + read_offset, frames_to_copy * sizeof(float)); - } -} - -/** Move audio data around. - * @param from Offset to move from. - * @param to Offset to move to. - * @param frames Number of frames to move. - */ - -void -AudioBuffers::move (int from, int to, int frames) -{ - if (frames == 0) { - return; - } - - assert (from >= 0); - assert (from < _frames); - assert (to >= 0); - assert (to < _frames); - assert (frames > 0); - assert (frames <= _frames); - assert ((from + frames) <= _frames); - assert ((to + frames) <= _frames); - - for (int i = 0; i < _channels; ++i) { - memmove (_data[i] + to, _data[i] + from, frames * sizeof(float)); - } -} - /** Trip an assert if the caller is not in the UI thread */ void ensure_ui_thread () { - assert (this_thread::get_id() == ui_thread); + assert (boost::this_thread::get_id() == ui_thread); } -/** @param v Source video frame. +/** @param v Content video frame. * @param audio_sample_rate Source audio sample rate. * @param frames_per_second Number of video frames per second. * @return Equivalent number of audio frames for `v'. */ int64_t -video_frames_to_audio_frames (SourceFrame v, float audio_sample_rate, float frames_per_second) +video_frames_to_audio_frames (ContentVideoFrame v, float audio_sample_rate, float frames_per_second) { return ((int64_t) v * audio_sample_rate / frames_per_second); } -/** @param f Filename. - * @return true if this file is a still image, false if it is something else. - */ -bool -still_image_file (string f) -{ - string ext = boost::filesystem::path(f).extension().string(); - - transform (ext.begin(), ext.end(), ext.begin(), ::tolower); - - return (ext == N_(".tif") || ext == N_(".tiff") || ext == N_(".jpg") || ext == N_(".jpeg") || ext == N_(".png") || ext == N_(".bmp")); -} - -/** @return A pair containing CPU model name and the number of processors */ -pair -cpu_info () -{ - pair info; - info.second = 0; - -#ifdef DVDOMATIC_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) { @@ -938,58 +735,6 @@ audio_channel_name (int c) return channels[c]; } -AudioMapping::AudioMapping (int c) - : _source_channels (c) -{ - -} - -optional -AudioMapping::source_to_dcp (int c) const -{ - if (c >= _source_channels) { - return optional (); - } - - if (_source_channels == 1) { - /* mono sources to centre */ - return libdcp::CENTRE; - } - - return static_cast (c); -} - -optional -AudioMapping::dcp_to_source (libdcp::Channel c) const -{ - if (_source_channels == 1) { - if (c == libdcp::CENTRE) { - return 0; - } else { - return optional (); - } - } - - if (static_cast (c) >= _source_channels) { - return optional (); - } - - return static_cast (c); -} - -int -AudioMapping::dcp_channels () const -{ - if (_source_channels == 1) { - /* The source is mono, so to put the mono channel into - the centre we need to generate a 5.1 soundtrack. - */ - return 6; - } - - return _source_channels; -} - FrameRateConversion::FrameRateConversion (float source, int dcp) : skip (false) , repeat (false) @@ -1018,3 +763,22 @@ FrameRateConversion::FrameRateConversion (float source, int dcp) } } } + +LocaleGuard::LocaleGuard () + : _old (0) +{ + char const * old = setlocale (LC_NUMERIC, 0); + + if (old) { + _old = strdup (old); + if (strcmp (_old, "C")) { + setlocale (LC_NUMERIC, "C"); + } + } +} + +LocaleGuard::~LocaleGuard () +{ + setlocale (LC_NUMERIC, _old); + free (_old); +}