Remove old DCP before creating new one (#47).
[dcpomatic.git] / src / lib / util.cc
index 4228ce6cfef66eccbcd1f4bc0c8f681b58734993..340b76b57bfaad0eb762ea98ba73a64497d351b8 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;
 
@@ -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 ();
@@ -244,7 +249,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 +361,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 +457,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);
@@ -583,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)
@@ -895,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);