Give DCPFrameRate a constructor. Add repeat member and cleverer dcp frame rate calcu...
authorCarl Hetherington <cth@carlh.net>
Wed, 16 Jan 2013 21:45:41 +0000 (21:45 +0000)
committerCarl Hetherington <cth@carlh.net>
Wed, 16 Jan 2013 21:45:41 +0000 (21:45 +0000)
src/lib/check_hashes_job.cc
src/lib/dcp_video_frame.cc
src/lib/encoder.cc
src/lib/film.cc
src/lib/make_dcp_job.cc
src/lib/options.h
src/lib/util.cc
src/lib/util.h

index 701584c74dfb7ca7c2f0f4b32f2623d0e57c50cb..6f6591396a13fca3702c6784b2743e4ebf05ca0d 100644 (file)
@@ -59,9 +59,11 @@ CheckHashesJob::run ()
        }
        
        SourceFrame const N = _film->dcp_trim_start() + _film->dcp_length().get();
-       DCPFrameRate const dfr = dcp_frame_rate (_film->frames_per_second ());
+       DCPFrameRate const dfr (_film->frames_per_second ());
+
+       int const inc = dfr.skip ? 2 : 1;
        
-       for (SourceFrame i = _film->dcp_trim_start(); i < N; i += dfr.skip) {
+       for (SourceFrame i = _film->dcp_trim_start(); i < N; i += inc) {
                string const j2k_file = _encode_opt->frame_out_path (i, false);
                string const hash_file = _encode_opt->hash_out_path (i, false);
 
index c6b29ba4169a62b8d61578caf1261c50262ad70a..f6fb8fe2e2d7eb5d457295426fcc0c208f9e4d42 100644 (file)
@@ -66,8 +66,8 @@ using boost::shared_ptr;
  *  @param out Required size of output, in pixels (including any padding).
  *  @param s Scaler to use.
  *  @param p Number of pixels of padding either side of the image.
- *  @param f Index of the frame within the Film.
- *  @param fps Frames per second of the Film.
+ *  @param f Index of the frame within the Film's source.
+ *  @param fps Frames per second of the Film's source.
  *  @param pp FFmpeg post-processing string to use.
  *  @param clut Colour look-up table to use (see Config::colour_lut_index ())
  *  @param bw J2K bandwidth to use (see Config::j2k_bandwidth ())
@@ -86,7 +86,7 @@ DCPVideoFrame::DCPVideoFrame (
        , _subtitle_scale (subtitle_scale)
        , _scaler (s)
        , _frame (f)
-       , _frames_per_second (dcp_frame_rate(fps).frames_per_second)
+       , _frames_per_second (DCPFrameRate(fps).frames_per_second)
        , _post_process (pp)
        , _colour_lut (clut)
        , _j2k_bandwidth (bw)
index efedfcfef76e18bd30923a3ca193ac25a19a3129..afa636150dc99e529d4258f045db7826747e7b2a 100644 (file)
@@ -279,7 +279,7 @@ Encoder::frame_skipped ()
 void
 Encoder::process_video (shared_ptr<Image> image, bool same, boost::shared_ptr<Subtitle> sub)
 {
-       if (_opt->video_skip != 0 && (_video_frame % _opt->video_skip) != 0) {
+       if (_opt->video_skip && (_video_frame % 2)) {
                ++_video_frame;
                return;
        }
index f0441c9e0b038733b86a7797cc32b3706cc5a028..17e14e544339e3b4546f73657f00a98173e18f80 100644 (file)
@@ -294,25 +294,26 @@ Film::make_dcp (bool transcode)
        if (dcp_length ()) {
                oe->video_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get());
                if (audio_stream()) {
+                       DCPFrameRate dfr (frames_per_second ());
                        oe->audio_range = make_pair (
 
                                video_frames_to_audio_frames (
                                        oe->video_range.get().first,
                                        dcp_audio_sample_rate (audio_stream()->sample_rate()),
-                                       dcp_frame_rate (frames_per_second()).frames_per_second
+                                       dfr.frames_per_second
                                        ),
                                
                                video_frames_to_audio_frames (
                                        oe->video_range.get().second,
                                        dcp_audio_sample_rate (audio_stream()->sample_rate()),
-                                       dcp_frame_rate (frames_per_second()).frames_per_second
+                                       dfr.frames_per_second
                                        )
                                );
                }
                        
        }
        
-       oe->video_skip = dcp_frame_rate (frames_per_second()).skip;
+       oe->video_skip = DCPFrameRate (frames_per_second()).skip;
 
        shared_ptr<DecodeOptions> od (new DecodeOptions);
        od->decode_subtitles = with_subtitles ();
@@ -704,13 +705,15 @@ Film::target_audio_sample_rate () const
        /* Resample to a DCI-approved sample rate */
        double t = dcp_audio_sample_rate (audio_stream()->sample_rate());
 
-       DCPFrameRate dfr = dcp_frame_rate (frames_per_second ());
+       DCPFrameRate dfr (frames_per_second ());
 
        /* Compensate for the fact that video will be rounded to the
           nearest integer number of frames per second.
        */
+
+       int const mult = dfr.skip ? 2 : 1;
        if (dfr.run_fast) {
-               t *= _frames_per_second * dfr.skip / dfr.frames_per_second;
+               t *= _frames_per_second * mult / dfr.frames_per_second;
        }
 
        return rint (t);
index b9f0cacf3b2d9f62219fdf0eff111890f7d91ddb..67a2d8b13e35e8f16d1565660a37b068bf61fa65 100644 (file)
@@ -61,7 +61,9 @@ MakeDCPJob::name () const
 string
 MakeDCPJob::j2c_path (int f, int offset) const
 {
-       SourceFrame const s = ((f + offset) * dcp_frame_rate(_film->frames_per_second()).skip) + _film->dcp_trim_start();
+       DCPFrameRate dfr (_film->frames_per_second());
+       int const mult = dfr.skip ? 2 : 1;
+       SourceFrame const s = ((f + offset) * mult) + _film->dcp_trim_start();
        return _opt->frame_out_path (s, false);
 }
 
@@ -85,13 +87,16 @@ MakeDCPJob::run ()
        /* Remove any old DCP */
        boost::filesystem::remove_all (dcp_path);
 
-       DCPFrameRate const dfr = dcp_frame_rate (_film->frames_per_second ());
+       DCPFrameRate const dfr (_film->frames_per_second ());
 
        int frames = 0;
        switch (_film->content_type ()) {
        case VIDEO:
                /* Source frames -> DCP frames */
-               frames = _film->dcp_length().get() / dfr.skip;
+               frames = _film->dcp_length().get();
+               if (dfr.skip) {
+                       frames /= 2;
+               }
                break;
        case STILL:
                frames = _film->still_duration() * 24;
index 55b066a2d90bb918d53b9a315b62d66fe8020a7b..d1fc1d54e7c6928010220e1296ffea279294a25c 100644 (file)
@@ -39,7 +39,7 @@ public:
 
        EncodeOptions (std::string f, std::string e, std::string m)
                : padding (0)
-               , video_skip (0)
+               , video_skip (false)
                , _frame_out_path (f)
                , _frame_out_extension (e)
                , _multichannel_audio_out_path (m)
@@ -98,10 +98,8 @@ public:
        /** Range of audio frames to decode (in the DCP's sampling rate) */
        boost::optional<std::pair<int64_t, int64_t> > audio_range;
        
-       /** Skip frames such that we don't decode any frame where (index % decode_video_skip) != 0; e.g.
-        *  1 for every frame, 2 for every other frame, etc.
-        */
-       SourceFrame video_skip; 
+       /** true to skip every other frame */
+       SourceFrame video_skip;
 
 private:
        /** Path of the directory to write video frames to */
index 0fced638cc47ad8954f8fe0ad9825d348e5a3498..4228ce6cfef66eccbcd1f4bc0c8f681b58734993 100644 (file)
@@ -330,25 +330,55 @@ 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)
-{
-       DCPFrameRate dfr;
-
-       dfr.run_fast = (fps != rint (fps));
-       dfr.frames_per_second = rint (fps);
-       dfr.skip = 1;
-
-       /* XXX: somewhat arbitrary */
-       if (fps == 50) {
-               dfr.frames_per_second = 25;
-               dfr.skip = 2;
+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);
+}
+
+/** @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;
        }
-
-       return dfr;
 }
 
 /** @param An arbitrary sampling rate.
index 024c40fb555f32b4aa3045bbcb944682629fb749..50e7410d67fb2047935b4aea47fe94cc92858c01 100644 (file)
@@ -61,14 +61,22 @@ typedef int SourceFrame;
 
 struct DCPFrameRate
 {
+       DCPFrameRate (float);
+       
        /** frames per second for the DCP */
        int frames_per_second;
-       /** Skip every `skip' frames.  e.g. if this is 1, we skip nothing;
-        *  if it's 2, we skip every other frame.
-        */
-       int skip;
+       /** true to skip every other frame */
+       bool skip;
+       /** true to repeat every frame once */
+       bool repeat;
        /** true if this DCP will run its video faster than the source
-        *  (e.g. if the source is 29.97fps and we will run the DCP at 30fps)
+        *  without taking into account `skip' and `repeat'.
+        *  (i.e. run_fast will be true if
+        *          source is 29.97fps, DCP is 30fps
+        *          source is 14.50fps, DCP is 30fps
+        *  but not if
+        *          source is 15.00fps, DCP is 30fps
+        *          source is 12.50fps, DCP is 25fps)
         */
        bool run_fast;
 };
@@ -183,7 +191,6 @@ struct Rect
 
 extern std::string crop_string (Position, Size);
 extern int dcp_audio_sample_rate (int);
-extern DCPFrameRate dcp_frame_rate (float);
 extern int dcp_audio_channels (int);
 extern std::string colour_lut_index_to_name (int index);
 extern int stride_round_up (int, int const *, int);