X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Futil.cc;h=340b76b57bfaad0eb762ea98ba73a64497d351b8;hb=90550787a8b00329170a6a1d0e9bb58aeb92ac6f;hp=7f370b89647557199657f47b4d47037ec5b552c0;hpb=71ea64782f215c605877f5c231a2a8b1838fe8bd;p=dcpomatic.git diff --git a/src/lib/util.cc b/src/lib/util.cc index 7f370b896..340b76b57 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef DVDOMATIC_POSIX #include #include @@ -58,9 +59,11 @@ extern "C" { #include "dcp_content_type.h" #include "filter.h" #include "sound_processor.h" +#include "config.h" using namespace std; using namespace boost; +using libdcp::Size; thread::id ui_thread; @@ -230,6 +233,8 @@ seconds (struct timeval t) void dvdomatic_setup () { + avfilter_register_all (); + Format::setup_formats (); DCPContentType::setup_dcp_content_types (); Scaler::setup_scalers (); @@ -330,25 +335,100 @@ md5_digest (string file) return s.str (); } -/** @param fps Arbitrary frames-per-second value. - * @return DCPFrameRate for this frames-per-second. - */ -DCPFrameRate -dcp_frame_rate (float fps) +static bool about_equal (float a, float b) +{ + /* A film of F seconds at f FPS will be Ff frames; + Consider some delta FPS d, so if we run the same + film at (f + d) FPS it will last F(f + d) seconds. + + Hence the difference in length over the length of the film will + be F(f + d) - Ff frames + = Ff + Fd - Ff frames + = Fd frames + = Fd/f seconds + + So if we accept a difference of 1 frame, ie 1/f seconds, we can + say that + + 1/f = Fd/f + ie 1 = Fd + ie d = 1/F + + So for a 3hr film, ie F = 3 * 60 * 60 = 10800, the acceptable + FPS error is 1/F ~= 0.0001 ~= 10-e4 + */ + + return (fabs (a - b) < 1e-4); +} + +class FrameRateCandidate +{ +public: + FrameRateCandidate (float source_, int dcp_) + : source (source_) + , dcp (dcp_) + {} + + bool skip () const { + return !about_equal (source, dcp) && source > dcp; + } + + bool repeat () const { + return !about_equal (source, dcp) && source < dcp; + } + + float source; + int dcp; +}; + +/** @param fps Arbitrary source frames-per-second value */ +/** XXX: this could be slow-ish */ +DCPFrameRate::DCPFrameRate (float source_fps) { - DCPFrameRate dfr; + 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; + } - dfr.run_fast = (fps != rint (fps)); - dfr.frames_per_second = rint (fps); - dfr.skip = 1; + float const e = fabs (i->source - source_fps); + if (e < error) { + error = e; + best = *i; + } - /* XXX: somewhat arbitrary */ - if (fps == 50) { - dfr.frames_per_second = 25; - dfr.skip = 2; + ++i; } - return dfr; + if (!best) { + throw EncodeError ("cannot find a suitable DCP frame rate for this source"); + } + + frames_per_second = best->dcp; + skip = best->skip (); + repeat = best->repeat (); + change_speed = !about_equal (source_fps * factor(), frames_per_second); } /** @param An arbitrary sampling rate. @@ -542,6 +622,9 @@ Socket::read_definite_and_consume (uint8_t* data, int size, int timeout) /** Read as much data as is available, up to some limit. * @param data Where to put the data. * @param size Maximum amount of data to read. + * + * XXX This method assumes that there is always lots of data to read(); + * if there isn't, it will hang waiting for data that will never arrive. */ void Socket::read_indefinite (uint8_t* data, int size, int timeout) @@ -854,11 +937,7 @@ video_frames_to_audio_frames (SourceFrame v, float audio_sample_rate, float fram bool still_image_file (string f) { -#if BOOST_FILESYSTEM_VERSION == 3 string ext = boost::filesystem::path(f).extension().string(); -#else - string ext = boost::filesystem::path(f).extension(); -#endif transform (ext.begin(), ext.end(), ext.begin(), ::tolower);