Merge master branch.
[dcpomatic.git] / src / lib / util.cc
index 4228ce6cfef66eccbcd1f4bc0c8f681b58734993..872985024ac9a5779bfab4f9fa6fdd4988984047 100644 (file)
@@ -26,6 +26,7 @@
 #include <iomanip>
 #include <iostream>
 #include <fstream>
+#include <climits>
 #ifdef DVDOMATIC_POSIX
 #include <execinfo.h>
 #include <cxxabi.h>
@@ -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;
 
@@ -244,7 +247,7 @@ dvdomatic_setup ()
  *  @return FFmpeg crop filter string.
  */
 string
-crop_string (Position start, Size size)
+crop_string (Position start, libdcp::Size size)
 {
        stringstream s;
        s << "crop=" << size.width << ":" << size.height << ":" << start.x << ":" << start.y;
@@ -356,29 +359,74 @@ static bool about_equal (float a, float b)
        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 */
-DCPFrameRate::DCPFrameRate (float fps)
-       : frames_per_second (rint (fps))
-       , skip (false)
-       , repeat (false)
-       , run_fast (false)
-{
-       if (about_equal (fps, 50)) {
-               /* XXX: not sure about this; just run at 50?
-                  Ring Peter Jackson.
-               */
-               frames_per_second = 25;
-               skip = true;
-       } else if (fps >= (27.5 / 2) && fps <= (32.5 / 2)) {
-               frames_per_second = 30;
-               repeat = true;
-       } else if (fps >= (24.5 / 2) && fps <= (27.5 / 2)) {
-               frames_per_second = 25;
-               repeat = true;
-       } else if (fps >= (20 / 2) && fps <= (24.5 / 2)) {
-               frames_per_second = 24;
-               repeat = true;
+/** XXX: this could be slow-ish */
+DCPFrameRate::DCPFrameRate (float source_fps)
+{
+       list<int> 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<FrameRateCandidate> candidates;
+
+       /* Start with the ones without skip / repeat so they will get matched in preference to skipped/repeated ones */
+       for (list<int>::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<int>::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<float>::max ();
+       boost::optional<FrameRateCandidate> best;
+       list<FrameRateCandidate>::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;
+       }
+
+       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.
@@ -407,17 +455,6 @@ dcp_audio_channels (int f)
        return f;
 }
 
-
-bool operator== (Size const & a, Size const & b)
-{
-       return (a.width == b.width && a.height == b.height);
-}
-
-bool operator!= (Size const & a, Size const & b)
-{
-       return !(a == b);
-}
-
 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);