Back-port v2's rename and slight extension of FrameRateConversion.
authorCarl Hetherington <cth@carlh.net>
Wed, 4 Jun 2014 12:10:28 +0000 (13:10 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 4 Jun 2014 12:10:28 +0000 (13:10 +0100)
12 files changed:
src/lib/audio_content.cc
src/lib/ffmpeg_content.cc
src/lib/frame_rate_change.cc [new file with mode: 0644]
src/lib/frame_rate_change.h [new file with mode: 0644]
src/lib/image_content.cc
src/lib/player.cc
src/lib/util.cc
src/lib/util.h
src/lib/video_content.cc
src/lib/wscript
src/wx/video_panel.cc
test/frame_rate_test.cc

index 79912f1aee7ec4eb1f86a2b208fd33eb0bba6c75..7d77154e17eaf044be7e5e9e9ea9751ede18f306 100644 (file)
@@ -25,6 +25,7 @@
 #include "film.h"
 #include "exceptions.h"
 #include "config.h"
+#include "frame_rate_change.h"
 
 #include "i18n.h"
 
@@ -159,7 +160,7 @@ AudioContent::output_audio_frame_rate () const
        /* Resample to a DCI-approved sample rate */
        double t = dcp_audio_frame_rate (content_audio_frame_rate ());
 
-       FrameRateConversion frc (video_frame_rate(), film->video_frame_rate());
+       FrameRateChange frc (video_frame_rate(), film->video_frame_rate());
 
        /* Compensate if the DCP is being run at a different frame rate
           to the source; that is, if the video is run such that it will
@@ -168,7 +169,7 @@ AudioContent::output_audio_frame_rate () const
        */
 
        if (frc.change_speed) {
-               t *= video_frame_rate() * frc.factor() / film->video_frame_rate();
+               t /= frc.speed_up;
        }
 
        return rint (t);
index 4ea6dbc6ac4c23f9c3f2cefd747848968b8a3749..4d886a6ddf224ceabc38d15369d9da1bb4261806 100644 (file)
@@ -31,6 +31,7 @@ extern "C" {
 #include "film.h"
 #include "log.h"
 #include "exceptions.h"
+#include "frame_rate_change.h"
 
 #include "i18n.h"
 
@@ -407,7 +408,7 @@ FFmpegContent::full_length () const
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
        
-       FrameRateConversion frc (video_frame_rate (), film->video_frame_rate ());
+       FrameRateChange frc (video_frame_rate (), film->video_frame_rate ());
        return video_length_after_3d_combine() * frc.factor() * TIME_HZ / film->video_frame_rate ();
 }
 
diff --git a/src/lib/frame_rate_change.cc b/src/lib/frame_rate_change.cc
new file mode 100644 (file)
index 0000000..3e9c4b5
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cmath>
+#include "frame_rate_change.h"
+#include "compose.hpp"
+
+#include "i18n.h"
+
+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);
+}
+
+
+FrameRateChange::FrameRateChange (float source, int dcp)
+       : skip (false)
+       , repeat (1)
+       , change_speed (false)
+{
+       if (fabs (source / 2.0 - dcp) < fabs (source - dcp)) {
+               /* The difference between source and DCP frame rate will be lower
+                  (i.e. better) if we skip.
+               */
+               skip = true;
+       } else if (fabs (source * 2 - dcp) < fabs (source - dcp)) {
+               /* The difference between source and DCP frame rate would be better
+                  if we repeated each frame once; it may be better still if we
+                  repeated more than once.  Work out the required repeat.
+               */
+               repeat = round (dcp / source);
+       }
+
+       speed_up = dcp / (source * factor());
+       change_speed = !about_equal (speed_up, 1.0);
+
+       if (!skip && repeat == 1 && !change_speed) {
+               description = _("Content and DCP have the same rate.\n");
+       } else {
+               if (skip) {
+                       description = _("DCP will use every other frame of the content.\n");
+               } else if (repeat == 2) {
+                       description = _("Each content frame will be doubled in the DCP.\n");
+               } else if (repeat > 2) {
+                       description = String::compose (_("Each content frame will be repeated %1 more times in the DCP.\n"), repeat - 1);
+               }
+
+               if (change_speed) {
+                       float const pc = dcp * 100 / (source * factor());
+                       description += String::compose (_("DCP will run at %1%% of the content speed.\n"), pc);
+               }
+       }
+}
diff --git a/src/lib/frame_rate_change.h b/src/lib/frame_rate_change.h
new file mode 100644 (file)
index 0000000..6165f68
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <string>
+
+struct FrameRateChange
+{
+       FrameRateChange (float, int);
+
+       /** @return factor by which to multiply a source frame rate
+           to get the effective rate after any skip or repeat has happened.
+       */
+       float factor () const {
+               if (skip) {
+                       return 0.5;
+               }
+
+               return repeat;
+       }
+
+       /** true to skip every other frame */
+       bool skip;
+       /** number of times to use each frame (e.g. 1 is normal, 2 means repeat each frame once, and so on) */
+       int repeat;
+       /** true if this DCP will run its video faster or slower than the source
+        *  without taking into account `repeat' nor `skip'.
+        *  (e.g. change_speed 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 change_speed;
+
+       /** Amount by which the video is being sped-up in the DCP; e.g. for a
+        *  24fps source in a 25fps DCP this would be 25/24.
+        */
+       float speed_up;
+
+       std::string description;
+};
index 3b87fcf008c4372114e77009e424dbdd30dd5cf6..6acf0bab924001eaa5b51e1d1349ce134e5e719c 100644 (file)
@@ -24,6 +24,7 @@
 #include "compose.hpp"
 #include "film.h"
 #include "job.h"
+#include "frame_rate_change.h"
 
 #include "i18n.h"
 
@@ -130,7 +131,7 @@ ImageContent::full_length () const
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
        
-       FrameRateConversion frc (video_frame_rate(), film->video_frame_rate ());
+       FrameRateChange frc (video_frame_rate(), film->video_frame_rate ());
        return video_length_after_3d_combine() * frc.factor() * TIME_HZ / video_frame_rate();
 }
 
index 68df8ea709afd905955be97dd410366092f5a612..20cea7e4a482bc9da9a4d9b134daf032b8790441 100644 (file)
@@ -36,6 +36,7 @@
 #include "log.h"
 #include "scaler.h"
 #include "player_video_frame.h"
+#include "frame_rate_change.h"
 
 #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
 
@@ -201,7 +202,7 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const ImageProxy>
        shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
        assert (content);
 
-       FrameRateConversion frc (content->video_frame_rate(), _film->video_frame_rate());
+       FrameRateChange frc (content->video_frame_rate(), _film->video_frame_rate());
        if (frc.skip && (frame % 2) == 1) {
                return;
        }
index bbe6f77e1f26e2e8542031d18911632ba83d8f67..fa7be179a95541ea58bf8c33fae3c4ab93483f68 100644 (file)
@@ -789,44 +789,6 @@ audio_channel_name (int c)
        return channels[c];
 }
 
-FrameRateConversion::FrameRateConversion (float source, int dcp)
-       : skip (false)
-       , repeat (1)
-       , change_speed (false)
-{
-       if (fabs (source / 2.0 - dcp) < fabs (source - dcp)) {
-               /* The difference between source and DCP frame rate will be lower
-                  (i.e. better) if we skip.
-               */
-               skip = true;
-       } else if (fabs (source * 2 - dcp) < fabs (source - dcp)) {
-               /* The difference between source and DCP frame rate would be better
-                  if we repeated each frame once; it may be better still if we
-                  repeated more than once.  Work out the required repeat.
-               */
-               repeat = round (dcp / source);
-       }
-
-       change_speed = !about_equal (source * factor(), dcp);
-
-       if (!skip && repeat == 1 && !change_speed) {
-               description = _("Content and DCP have the same rate.\n");
-       } else {
-               if (skip) {
-                       description = _("DCP will use every other frame of the content.\n");
-               } else if (repeat == 2) {
-                       description = _("Each content frame will be doubled in the DCP.\n");
-               } else if (repeat > 2) {
-                       description = String::compose (_("Each content frame will be repeated %1 more times in the DCP.\n"), repeat - 1);
-               }
-
-               if (change_speed) {
-                       float const pc = dcp * 100 / (source * factor());
-                       description += String::compose (_("DCP will run at %1%% of the content speed.\n"), pc);
-               }
-       }
-}
-
 bool
 valid_image_file (boost::filesystem::path f)
 {
index 5d93456df6c043d2bac41d6b070b81e6f83ebcc6..70bf495c682c42332ea22bf5294497c243ad5276 100644 (file)
@@ -73,40 +73,6 @@ extern boost::shared_ptr<const libdcp::Signer> make_signer ();
 extern libdcp::Size fit_ratio_within (float ratio, libdcp::Size);
 extern std::string entities_to_text (std::string e);
 extern std::map<std::string, std::string> split_get_request (std::string url);
-
-struct FrameRateConversion
-{
-       FrameRateConversion (float, int);
-
-       /** @return factor by which to multiply a source frame rate
-           to get the effective rate after any skip or repeat has happened.
-       */
-       float factor () const {
-               if (skip) {
-                       return 0.5;
-               }
-
-               return repeat;
-       }
-
-       /** true to skip every other frame */
-       bool skip;
-       /** number of times to use each frame (e.g. 1 is normal, 2 means repeat each frame once, and so on) */
-       int repeat;
-       /** true if this DCP will run its video faster or slower than the source
-        *  without taking into account `repeat' nor `skip'.
-        *  (e.g. change_speed 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 change_speed;
-
-       std::string description;
-};
-
 extern int dcp_audio_frame_rate (int);
 extern int stride_round_up (int, int const *, int);
 extern std::multimap<std::string, std::string> read_key_value (std::istream& s);
index 40772980fb466754fdec79cd85699a082b33c7ae..ec5890b4549c1a43d5873d0035417a95bfa9a814 100644 (file)
@@ -30,6 +30,7 @@
 #include "util.h"
 #include "film.h"
 #include "exceptions.h"
+#include "frame_rate_change.h"
 
 #include "i18n.h"
 
@@ -367,7 +368,7 @@ VideoContent::time_to_content_video_frames (Time t) const
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
        
-       FrameRateConversion frc (video_frame_rate(), film->video_frame_rate());
+       FrameRateChange frc (video_frame_rate(), film->video_frame_rate());
 
        /* Here we are converting from time (in the DCP) to a frame number in the content.
           Hence we need to use the DCP's frame rate and the double/skip correction, not
index f932a142da6b161808d456117b662ad1fec325dd..517ad7787b9ce164273c44c46cecc7c99d2c2a3e 100644 (file)
@@ -30,6 +30,7 @@ sources = """
           ffmpeg_examiner.cc
           film.cc
           filter.cc
+          frame_rate_change.cc
           internet.cc
           image.cc
           image_content.cc
index ac4c29222b231e9912ca1e7306bbd91f253c8ac2..2d874b9594e8997571516d805d3d396df253b366 100644 (file)
@@ -24,6 +24,7 @@
 #include "lib/config.h"
 #include "lib/util.h"
 #include "lib/ratio.h"
+#include "lib/frame_rate_change.h"
 #include "filter_dialog.h"
 #include "video_panel.h"
 #include "wx_util.h"
@@ -330,7 +331,7 @@ VideoPanel::setup_description ()
 
        d << wxString::Format (_("Content frame rate %.4f\n"), vcs->video_frame_rate ());
        ++lines;
-       FrameRateConversion frc (vcs->video_frame_rate(), _editor->film()->video_frame_rate ());
+       FrameRateChange frc (vcs->video_frame_rate(), _editor->film()->video_frame_rate ());
        d << std_to_wx (frc.description) << "\n";
        ++lines;
 
index fdfdcf4529739f126ffab9da192aafeec44f8503..8981a9d7e330e3eca69c84ace2a90215cb8cfc24 100644 (file)
 #include "lib/config.h"
 #include "lib/ffmpeg_content.h"
 #include "lib/playlist.h"
+#include "lib/frame_rate_change.h"
 #include "test.h"
 
 using boost::shared_ptr;
 
-/* Test Playlist::best_dcp_frame_rate and FrameRateConversion
+/* Test Playlist::best_dcp_frame_rate and FrameRateChange
    with a single piece of content.
 */
 BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
@@ -47,7 +48,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 60;
        int best = film->playlist()->best_dcp_frame_rate ();
-       FrameRateConversion frc = FrameRateConversion (60, best);
+       FrameRateChange frc = FrameRateChange (60, best);
        BOOST_CHECK_EQUAL (best, 30);
        BOOST_CHECK_EQUAL (frc.skip, true);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
@@ -55,7 +56,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        
        content->_video_frame_rate = 50;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (50, best);
+       frc = FrameRateChange (50, best);
        BOOST_CHECK_EQUAL (best, 25);
        BOOST_CHECK_EQUAL (frc.skip, true);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
@@ -63,7 +64,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 48;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (48, best);
+       frc = FrameRateChange (48, best);
        BOOST_CHECK_EQUAL (best, 24);
        BOOST_CHECK_EQUAL (frc.skip, true);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
@@ -71,7 +72,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 30;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (30, best);
+       frc = FrameRateChange (30, best);
        BOOST_CHECK_EQUAL (best, 30);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
@@ -79,7 +80,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 29.97;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (29.97, best);
+       frc = FrameRateChange (29.97, best);
        BOOST_CHECK_EQUAL (best, 30);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
@@ -87,7 +88,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        
        content->_video_frame_rate = 25;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (25, best);
+       frc = FrameRateChange (25, best);
        BOOST_CHECK_EQUAL (best, 25);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
@@ -95,7 +96,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 24;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (24, best);
+       frc = FrameRateChange (24, best);
        BOOST_CHECK_EQUAL (best, 24);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
@@ -103,7 +104,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 14.5;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (14.5, best);
+       frc = FrameRateChange (14.5, best);
        BOOST_CHECK_EQUAL (best, 30);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 2);
@@ -111,7 +112,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 12.6;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (12.6, best);
+       frc = FrameRateChange (12.6, best);
        BOOST_CHECK_EQUAL (best, 25);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 2);
@@ -119,7 +120,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 12.4;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (12.4, best);
+       frc = FrameRateChange (12.4, best);
        BOOST_CHECK_EQUAL (best, 25);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 2);
@@ -127,7 +128,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 12;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (12, best);
+       frc = FrameRateChange (12, best);
        BOOST_CHECK_EQUAL (best, 24);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 2);
@@ -144,7 +145,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 60;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (60, best);
+       frc = FrameRateChange (60, best);
        BOOST_CHECK_EQUAL (best, 60);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
@@ -152,7 +153,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        
        content->_video_frame_rate = 50;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (50, best);
+       frc = FrameRateChange (50, best);
        BOOST_CHECK_EQUAL (best, 50);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
@@ -160,7 +161,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 48;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (48, best);
+       frc = FrameRateChange (48, best);
        BOOST_CHECK_EQUAL (best, 48);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
@@ -168,7 +169,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        /* Check some out-there conversions (not the best) */
        
-       frc = FrameRateConversion (14.99, 24);
+       frc = FrameRateChange (14.99, 24);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 2);
        BOOST_CHECK_EQUAL (frc.change_speed, true);
@@ -181,14 +182,14 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
 
        content->_video_frame_rate = 25;
        best = film->playlist()->best_dcp_frame_rate ();
-       frc = FrameRateConversion (25, best);
+       frc = FrameRateChange (25, best);
        BOOST_CHECK_EQUAL (best, 24);
        BOOST_CHECK_EQUAL (frc.skip, false);
        BOOST_CHECK_EQUAL (frc.repeat, 1);
        BOOST_CHECK_EQUAL (frc.change_speed, true);
 }
 
-/* Test Playlist::best_dcp_frame_rate and FrameRateConversion
+/* Test Playlist::best_dcp_frame_rate and FrameRateChange
    with two pieces of content.
 */
 BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_double)
@@ -266,7 +267,7 @@ BOOST_AUTO_TEST_CASE (audio_sampling_rate_test)
        content->_video_frame_rate = 14.99;
        film->set_video_frame_rate (25);
        content->set_audio_stream (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream ("a", 42, 16000, 0)));
-       /* The FrameRateConversion within output_audio_frame_rate should choose to double-up
+       /* The FrameRateChange within output_audio_frame_rate should choose to double-up
           the 14.99 fps video to 30 and then run it slow at 25.
        */
        BOOST_CHECK_EQUAL (content->output_audio_frame_rate(), rint (48000 * 2 * 14.99 / 25));