Merge master.
authorCarl Hetherington <cth@carlh.net>
Sat, 2 Mar 2013 20:12:50 +0000 (20:12 +0000)
committerCarl Hetherington <cth@carlh.net>
Sat, 2 Mar 2013 20:12:50 +0000 (20:12 +0000)
25 files changed:
ChangeLog
src/lib/ab_transcoder.cc
src/lib/analyse_audio_job.cc
src/lib/config.cc
src/lib/dcp_video_frame.cc
src/lib/dcp_video_frame.h
src/lib/encoder.cc
src/lib/film.cc
src/lib/film.h
src/lib/format.cc
src/lib/format.h
src/lib/transcode_job.cc
src/lib/transcoder.cc
src/lib/util.cc
src/lib/util.h
src/lib/writer.cc
src/wx/audio_plot.cc
src/wx/film_editor.cc
src/wx/film_editor.h
src/wx/film_viewer.cc
src/wx/properties_dialog.cc
src/wx/wx_util.cc
src/wx/wx_util.h
test/metadata.ref
test/test.cc

index b234f5baacc75b569f53853a522917dc3e50052e..2d380a8df4cbcd55754ffb24c5cd1f37c5b78500 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2013-03-02  Carl Hetherington  <cth@carlh.net>
+
+       * Add option to specify the DCP's frame
+       rate (part of #56).
+
+       * Add a description of what each video format
+       means to the UI.
+
 2013-03-01  Carl Hetherington  <cth@carlh.net>
 
        * Version 0.76beta2 released.
index f47a99fda8732f504d32f6ee56ffe9a7e08be950..4ed5d02ca7018e3f90048448c49114dd2106253c 100644 (file)
@@ -60,7 +60,7 @@ ABTranscoder::ABTranscoder (
 
        if (_film_a->audio_stream()) {
                shared_ptr<AudioStream> st = _film_a->audio_stream();
-               _matcher.reset (new Matcher (_film_a->log(), st->sample_rate(), _film_a->frames_per_second()));
+               _matcher.reset (new Matcher (_film_a->log(), st->sample_rate(), _film_a->source_frame_rate()));
                _delay_line.reset (new DelayLine (_film_a->log(), st->channels(), _film_a->audio_delay() * st->sample_rate() / 1000));
                _gain.reset (new Gain (_film_a->log(), _film_a->audio_gain()));
        }
index ca316f70ebe80d2ce4ccc2734dbd9f99459cb570..41f918f34ae81b068fbf86f2f3bd64b27c87b5f4 100644 (file)
@@ -66,7 +66,7 @@ AnalyseAudioJob::run ()
        decoders.audio->set_audio_stream (_film->audio_stream ());
        decoders.audio->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1));
 
-       int64_t total_audio_frames = video_frames_to_audio_frames (_film->length().get(), _film->audio_stream()->sample_rate(), _film->frames_per_second());
+       int64_t total_audio_frames = video_frames_to_audio_frames (_film->length().get(), _film->audio_stream()->sample_rate(), _film->source_frame_rate());
        _samples_per_point = total_audio_frames / _num_points;
 
        _current.resize (_film->audio_stream()->channels ());
index 82a31b3cf0a98d23d191f4906776457abff32903..f5273b8753977449fc3334af769e35cf491a8ef1 100644 (file)
@@ -49,6 +49,9 @@ Config::Config ()
        _allowed_dcp_frame_rates.push_back (24);
        _allowed_dcp_frame_rates.push_back (25);
        _allowed_dcp_frame_rates.push_back (30);
+       _allowed_dcp_frame_rates.push_back (48);
+       _allowed_dcp_frame_rates.push_back (50);
+       _allowed_dcp_frame_rates.push_back (60);
        
        ifstream f (file().c_str ());
        string line;
index 098d222cdc87bad36d98654f2cb04221c15dda20..67617c63ccf5c75dab214d26853ef84c9ebfd335 100644 (file)
@@ -80,7 +80,7 @@ using libdcp::Size;
 DCPVideoFrame::DCPVideoFrame (
        shared_ptr<const Image> yuv, shared_ptr<Subtitle> sub,
        Size out, int p, int subtitle_offset, float subtitle_scale,
-       Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l
+       Scaler const * s, int f, int dcp_fps, string pp, int clut, int bw, Log* l
        )
        : _input (yuv)
        , _subtitle (sub)
@@ -90,7 +90,7 @@ DCPVideoFrame::DCPVideoFrame (
        , _subtitle_scale (subtitle_scale)
        , _scaler (s)
        , _frame (f)
-       , _frames_per_second (DCPFrameRate(fps).frames_per_second)
+       , _frames_per_second (dcp_fps)
        , _post_process (pp)
        , _colour_lut (clut)
        , _j2k_bandwidth (bw)
index ab458b58f8124953b8ba390d2fc5fb27d330f58a..6794765acefbc1f7a7f85a9cb57449fa1d3e5fe4 100644 (file)
@@ -107,7 +107,7 @@ class DCPVideoFrame
 public:
        DCPVideoFrame (
                boost::shared_ptr<const Image>, boost::shared_ptr<Subtitle>, libdcp::Size,
-               int, int, float, Scaler const *, int, float, std::string, int, int, Log *
+               int, int, float, Scaler const *, int, int, std::string, int, int, Log *
                );
        
        virtual ~DCPVideoFrame ();
@@ -130,7 +130,7 @@ private:
        float _subtitle_scale;
        Scaler const * _scaler;          ///< scaler to use
        int _frame;                      ///< frame index within the DCP's intrinsic duration
-       int _frames_per_second;          ///< Frames per second that we will use for the DCP (rounded)
+       int _frames_per_second;          ///< Frames per second that we will use for the DCP
        std::string _post_process;       ///< FFmpeg post-processing string to use
        int _colour_lut;                 ///< Colour look-up table to use
        int _j2k_bandwidth;              ///< J2K bandwidth to use
index 3cc643cd6cf3fca7855611cff6a35f074bef72cd..687dfdd2b0ee2bbab2db1d94b76b76b67bd34650 100644 (file)
@@ -233,9 +233,9 @@ Encoder::frame_done ()
 void
 Encoder::process_video (shared_ptr<Image> image, bool same, boost::shared_ptr<Subtitle> sub)
 {
-       DCPFrameRate dfr (_film->frames_per_second ());
+       FrameRateConversion frc (_film->source_frame_rate(), _film->dcp_frame_rate());
        
-       if (dfr.skip && (_video_frames_in % 2)) {
+       if (frc.skip && (_video_frames_in % 2)) {
                ++_video_frames_in;
                return;
        }
@@ -273,7 +273,7 @@ Encoder::process_video (shared_ptr<Image> image, bool same, boost::shared_ptr<Su
                                          new DCPVideoFrame (
                                                  image, sub, _film->format()->dcp_size(), _film->format()->dcp_padding (_film),
                                                  _film->subtitle_offset(), _film->subtitle_scale(),
-                                                 _film->scaler(), _video_frames_out, _film->frames_per_second(), s.second,
+                                                 _film->scaler(), _video_frames_out, _film->dcp_frame_rate(), s.second,
                                                  _film->colour_lut(), _film->j2k_bandwidth(),
                                                  _film->log()
                                                  )
@@ -286,7 +286,7 @@ Encoder::process_video (shared_ptr<Image> image, bool same, boost::shared_ptr<Su
        ++_video_frames_in;
        ++_video_frames_out;
 
-       if (dfr.repeat) {
+       if (frc.repeat) {
                _writer->repeat (_video_frames_out);
                ++_video_frames_out;
                frame_done ();
index 510158e942ad010e3ffcf87aa56a91e7ae656e96..8f545952b336f512e363a9f87d8d00f4a29faade 100644 (file)
@@ -66,6 +66,7 @@ using std::ofstream;
 using std::setfill;
 using std::min;
 using std::make_pair;
+using std::endl;
 using boost::shared_ptr;
 using boost::lexical_cast;
 using boost::to_upper_copy;
@@ -74,7 +75,7 @@ using boost::starts_with;
 using boost::optional;
 using libdcp::Size;
 
-int const Film::state_version = 3;
+int const Film::state_version = 4;
 
 /** Construct a Film object in a given directory, reading any metadata
  *  file that exists in that directory.  An exception will be thrown if
@@ -89,7 +90,7 @@ Film::Film (string d, bool must_exist)
        , _trust_content_header (true)
        , _dcp_content_type (0)
        , _format (0)
-       , _scaler (Scaler::from_id (N_("bicubic")))
+       , _scaler (Scaler::from_id ("bicubic"))
        , _trim_start (0)
        , _trim_end (0)
        , _dcp_ab (false)
@@ -103,7 +104,8 @@ Film::Film (string d, bool must_exist)
        , _colour_lut (0)
        , _j2k_bandwidth (200000000)
        , _dci_metadata (Config::instance()->default_dci_metadata ())
-       , _frames_per_second (0)
+       , _dcp_frame_rate (0)
+       , _source_frame_rate (0)
        , _dirty (false)
 {
        set_dci_date_today ();
@@ -115,13 +117,13 @@ Film::Film (string d, bool must_exist)
        boost::filesystem::path p (boost::filesystem::system_complete (d));
        boost::filesystem::path result;
        for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
-               if (*i == N_("..")) {
-                       if (boost::filesystem::is_symlink (result) || result.filename() == N_("..")) {
+               if (*i == "..") {
+                       if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
                                result /= *i;
                        } else {
                                result = result.parent_path ();
                        }
-               } else if (*i != N_(".")) {
+               } else if (*i != ".") {
                        result /= *i;
                }
        }
@@ -142,7 +144,7 @@ Film::Film (string d, bool must_exist)
                read_metadata ();
        }
 
-       _log = new FileLog (file (N_("log")));
+       _log = new FileLog (file ("log"));
 }
 
 Film::Film (Film const & o)
@@ -175,6 +177,7 @@ Film::Film (Film const & o)
        , _j2k_bandwidth     (o._j2k_bandwidth)
        , _dci_metadata      (o._dci_metadata)
        , _dci_date          (o._dci_date)
+       , _dcp_frame_rate    (o._dcp_frame_rate)
        , _size              (o._size)
        , _length            (o._length)
        , _dcp_intrinsic_duration (o._dcp_intrinsic_duration)
@@ -182,7 +185,7 @@ Film::Film (Film const & o)
        , _content_audio_streams (o._content_audio_streams)
        , _external_audio_stream (o._external_audio_stream)
        , _subtitle_streams  (o._subtitle_streams)
-       , _frames_per_second (o._frames_per_second)
+       , _source_frame_rate (o._source_frame_rate)
        , _dirty             (o._dirty)
 {
 
@@ -202,16 +205,17 @@ Film::video_state_identifier () const
 
        stringstream s;
        s << format()->id()
-         << N_("_") << content_digest()
-         << N_("_") << crop().left << N_("_") << crop().right << N_("_") << crop().top << N_("_") << crop().bottom
-         << N_("_") << f.first << N_("_") << f.second
-         << N_("_") << scaler()->id()
-         << N_("_") << j2k_bandwidth()
-         << N_("_") << boost::lexical_cast<int> (colour_lut());
+         << "_" << content_digest()
+         << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
+         << "_" << _dcp_frame_rate
+         << "_" << f.first << "_" << f.second
+         << "_" << scaler()->id()
+         << "_" << j2k_bandwidth()
+         << "_" << boost::lexical_cast<int> (colour_lut());
 
        if (dcp_ab()) {
                pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
-               s << N_("ab_") << Config::instance()->reference_scaler()->id() << N_("_") << fa.first << N_("_") << fa.second;
+               s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
        }
 
        return s.str ();
@@ -222,7 +226,7 @@ string
 Film::info_dir () const
 {
        boost::filesystem::path p;
-       p /= N_("info");
+       p /= "info";
        p /= video_state_identifier ();
        return dir (p.string());
 }
@@ -231,13 +235,13 @@ string
 Film::video_mxf_dir () const
 {
        boost::filesystem::path p;
-       return dir (N_("video"));
+       return dir ("video");
 }
 
 string
 Film::video_mxf_filename () const
 {
-       return video_state_identifier() + N_(".mxf");
+       return video_state_identifier() + ".mxf";
 }
 
 string
@@ -255,37 +259,38 @@ Film::make_dcp ()
 {
        set_dci_date_today ();
        
-       if (dcp_name().find (N_("/")) != string::npos) {
+       if (dcp_name().find ("/") != string::npos) {
                throw BadSettingError (_("name"), _("cannot contain slashes"));
        }
        
-       log()->log (String::compose (N_("DVD-o-matic %1 git %2 using %3"), dvdomatic_version, dvdomatic_git_commit, dependency_version_summary()));
+       log()->log (String::compose ("DVD-o-matic %1 git %2 using %3", dvdomatic_version, dvdomatic_git_commit, dependency_version_summary()));
 
        {
                char buffer[128];
                gethostname (buffer, sizeof (buffer));
-               log()->log (String::compose (N_("Starting to make DCP on %1"), buffer));
+               log()->log (String::compose ("Starting to make DCP on %1", buffer));
        }
        
-       log()->log (String::compose (N_("Content is %1; type %2"), content_path(), (content_type() == STILL ? _("still") : _("video"))));
+       log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? _("still") : _("video"))));
        if (length()) {
-               log()->log (String::compose (N_("Content length %1"), length().get()));
+               log()->log (String::compose ("Content length %1", length().get()));
        }
-       log()->log (String::compose (N_("Content digest %1"), content_digest()));
-       log()->log (String::compose (N_("%1 threads"), Config::instance()->num_local_encoding_threads()));
-       log()->log (String::compose (N_("J2K bandwidth %1"), j2k_bandwidth()));
+       log()->log (String::compose ("Content digest %1", content_digest()));
+       log()->log (String::compose ("Content at %1 fps, DCP at %2 fps", source_frame_rate(), dcp_frame_rate()));
+       log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads()));
+       log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth()));
 #ifdef DVDOMATIC_DEBUG
-       log()->log (N_("DVD-o-matic built in debug mode."));
+       log()->log ("DVD-o-matic built in debug mode.");
 #else
-       log()->log (N_("DVD-o-matic built in optimised mode."));
+       log()->log ("DVD-o-matic built in optimised mode.");
 #endif
 #ifdef LIBDCP_DEBUG
-       log()->log (N_("libdcp built in debug mode."));
+       log()->log ("libdcp built in debug mode.");
 #else
-       log()->log (N_("libdcp built in optimised mode."));
+       log()->log ("libdcp built in optimised mode.");
 #endif
        pair<string, int> const c = cpu_info ();
-       log()->log (String::compose (N_("CPU: %1, %2 processors"), c.first, c.second));
+       log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second));
        
        if (format() == 0) {
                throw MissingSettingError (_("format"));
@@ -391,73 +396,74 @@ Film::write_metadata () const
 
        boost::filesystem::create_directories (directory());
 
-       string const m = file (N_("metadata"));
+       string const m = file ("metadata");
        ofstream f (m.c_str ());
        if (!f.good ()) {
                throw CreateFileError (m);
        }
 
-       f << N_("version ") << state_version << N_("\n");
+       f << "version " << state_version << endl;
 
        /* User stuff */
-       f << N_("name ") << _name << N_("\n");
-       f << N_("use_dci_name ") << _use_dci_name << N_("\n");
-       f << N_("content ") << _content << N_("\n");
-       f << N_("trust_content_header ") << (_trust_content_header ? N_("1") : N_("0")) << N_("\n");
+       f << "name " << _name << endl;
+       f << "use_dci_name " << _use_dci_name << endl;
+       f << "content " << _content << endl;
+       f << "trust_content_header " << (_trust_content_header ? "1" : "0") << endl;
        if (_dcp_content_type) {
-               f << N_("dcp_content_type ") << _dcp_content_type->dci_name () << N_("\n");
+               f << "dcp_content_type " << _dcp_content_type->dci_name () << endl;
        }
        if (_format) {
-               f << N_("format ") << _format->as_metadata () << N_("\n");
+               f << "format " << _format->as_metadata () << endl;
        }
-       f << N_("left_crop ") << _crop.left << N_("\n");
-       f << N_("right_crop ") << _crop.right << N_("\n");
-       f << N_("top_crop ") << _crop.top << N_("\n");
-       f << N_("bottom_crop ") << _crop.bottom << N_("\n");
+       f << "left_crop " << _crop.left << endl;
+       f << "right_crop " << _crop.right << endl;
+       f << "top_crop " << _crop.top << endl;
+       f << "bottom_crop " << _crop.bottom << endl;
        for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
-               f << N_("filter ") << (*i)->id () << N_("\n");
+               f << "filter " << (*i)->id () << endl;
        }
-       f << N_("scaler ") << _scaler->id () << N_("\n");
-       f << N_("trim_start ") << _trim_start << N_("\n");
-       f << N_("trim_end ") << _trim_end << N_("\n");
-       f << N_("dcp_ab ") << (_dcp_ab ? N_("1") : N_("0")) << N_("\n");
+       f << "scaler " << _scaler->id () << endl;
+       f << "trim_start " << _trim_start << endl;
+       f << "trim_end " << _trim_end << endl;
+       f << "dcp_ab " << (_dcp_ab ? "1" : "0") << endl;
        if (_content_audio_stream) {
-               f << N_("selected_content_audio_stream ") << _content_audio_stream->to_string() << N_("\n");
+               f << "selected_content_audio_stream " << _content_audio_stream->to_string() << endl;
        }
        for (vector<string>::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) {
-               f << N_("external_audio ") << *i << N_("\n");
+               f << "external_audio " << *i << endl;
        }
-       f << N_("use_content_audio ") << (_use_content_audio ? N_("1") : N_("0")) << N_("\n");
-       f << N_("audio_gain ") << _audio_gain << N_("\n");
-       f << N_("audio_delay ") << _audio_delay << N_("\n");
-       f << N_("still_duration ") << _still_duration << N_("\n");
+       f << "use_content_audio " << (_use_content_audio ? "1" : "0") << endl;
+       f << "audio_gain " << _audio_gain << endl;
+       f << "audio_delay " << _audio_delay << endl;
+       f << "still_duration " << _still_duration << endl;
        if (_subtitle_stream) {
-               f << N_("selected_subtitle_stream ") << _subtitle_stream->to_string() << N_("\n");
+               f << "selected_subtitle_stream " << _subtitle_stream->to_string() << endl;
        }
-       f << N_("with_subtitles ") << _with_subtitles << N_("\n");
-       f << N_("subtitle_offset ") << _subtitle_offset << N_("\n");
-       f << N_("subtitle_scale ") << _subtitle_scale << N_("\n");
-       f << N_("colour_lut ") << _colour_lut << N_("\n");
-       f << N_("j2k_bandwidth ") << _j2k_bandwidth << N_("\n");
+       f << "with_subtitles " << _with_subtitles << endl;
+       f << "subtitle_offset " << _subtitle_offset << endl;
+       f << "subtitle_scale " << _subtitle_scale << endl;
+       f << "colour_lut " << _colour_lut << endl;
+       f << "j2k_bandwidth " << _j2k_bandwidth << endl;
        _dci_metadata.write (f);
-       f << N_("dci_date ") << boost::gregorian::to_iso_string (_dci_date) << N_("\n");
-       f << N_("width ") << _size.width << N_("\n");
-       f << N_("height ") << _size.height << N_("\n");
-       f << N_("length ") << _length.get_value_or(0) << N_("\n");
-       f << N_("dcp_intrinsic_duration ") << _dcp_intrinsic_duration.get_value_or(0) << N_("\n");
-       f << N_("content_digest ") << _content_digest << N_("\n");
+       f << "dci_date " << boost::gregorian::to_iso_string (_dci_date) << endl;
+       f << "dcp_frame_rate " << _dcp_frame_rate << endl;
+       f << "width " << _size.width << endl;
+       f << "height " << _size.height << endl;
+       f << "length " << _length.get_value_or(0) << endl;
+       f << "dcp_intrinsic_duration " << _dcp_intrinsic_duration.get_value_or(0) << endl;
+       f << "content_digest " << _content_digest << endl;
 
        for (vector<shared_ptr<AudioStream> >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
-               f << N_("content_audio_stream ") << (*i)->to_string () << N_("\n");
+               f << "content_audio_stream " << (*i)->to_string () << endl;
        }
 
-       f << N_("external_audio_stream ") << _external_audio_stream->to_string() << N_("\n");
+       f << "external_audio_stream " << _external_audio_stream->to_string() << endl;
 
        for (vector<shared_ptr<SubtitleStream> >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
-               f << N_("subtitle_stream ") << (*i)->to_string () << N_("\n");
+               f << "subtitle_stream " << (*i)->to_string () << endl;
        }
 
-       f << N_("frames_per_second ") << _frames_per_second << N_("\n");
+       f << "source_frame_rate " << _source_frame_rate << endl;
        
        _dirty = false;
 }
@@ -479,15 +485,15 @@ Film::read_metadata ()
        boost::optional<int> audio_stream_index;
        boost::optional<int> subtitle_stream_index;
 
-       ifstream f (file (N_("metadata")).c_str());
+       ifstream f (file ("metadata").c_str());
        if (!f.good()) {
-               throw OpenFileError (file (N_("metadata")));
+               throw OpenFileError (file ("metadata"));
        }
        
        multimap<string, string> kv = read_key_value (f);
 
        /* We need version before anything else */
-       multimap<string, string>::iterator v = kv.find (N_("version"));
+       multimap<string, string>::iterator v = kv.find ("version");
        if (v != kv.end ()) {
                version = atoi (v->second.c_str());
        }
@@ -496,108 +502,114 @@ Film::read_metadata ()
                string const k = i->first;
                string const v = i->second;
 
-               if (k == N_("audio_sample_rate")) {
+               if (k == "audio_sample_rate") {
                        audio_sample_rate = atoi (v.c_str());
                }
 
                /* User-specified stuff */
-               if (k == N_("name")) {
+               if (k == "name") {
                        _name = v;
-               } else if (k == N_("use_dci_name")) {
-                       _use_dci_name = (v == N_("1"));
-               } else if (k == N_("content")) {
+               } else if (k == "use_dci_name") {
+                       _use_dci_name = (v == "1");
+               } else if (k == "content") {
                        _content = v;
-               } else if (k == N_("trust_content_header")) {
-                       _trust_content_header = (v == N_("1"));
-               } else if (k == N_("dcp_content_type")) {
+               } else if (k == "trust_content_header") {
+                       _trust_content_header = (v == "1");
+               } else if (k == "dcp_content_type") {
                        if (version < 3) {
                                _dcp_content_type = DCPContentType::from_pretty_name (v);
                        } else {
                                _dcp_content_type = DCPContentType::from_dci_name (v);
                        }
-               } else if (k == N_("format")) {
+               } else if (k == "format") {
                        _format = Format::from_metadata (v);
-               } else if (k == N_("left_crop")) {
+               } else if (k == "left_crop") {
                        _crop.left = atoi (v.c_str ());
-               } else if (k == N_("right_crop")) {
+               } else if (k == "right_crop") {
                        _crop.right = atoi (v.c_str ());
-               } else if (k == N_("top_crop")) {
+               } else if (k == "top_crop") {
                        _crop.top = atoi (v.c_str ());
-               } else if (k == N_("bottom_crop")) {
+               } else if (k == "bottom_crop") {
                        _crop.bottom = atoi (v.c_str ());
-               } else if (k == N_("filter")) {
+               } else if (k == "filter") {
                        _filters.push_back (Filter::from_id (v));
-               } else if (k == N_("scaler")) {
+               } else if (k == "scaler") {
                        _scaler = Scaler::from_id (v);
-               } else if ( ((!version || version < 2) && k == N_("dcp_trim_start")) || k == N_("trim_start")) {
+               } else if ( ((!version || version < 2) && k == "dcp_trim_start") || k == "trim_start") {
                        _trim_start = atoi (v.c_str ());
-               } else if ( ((!version || version < 2) && k == N_("dcp_trim_end")) || k == N_("trim_end")) {
+               } else if ( ((!version || version < 2) && k == "dcp_trim_end") || k == "trim_end") {
                        _trim_end = atoi (v.c_str ());
-               } else if (k == N_("dcp_ab")) {
-                       _dcp_ab = (v == N_("1"));
-               } else if (k == N_("selected_content_audio_stream") || (!version && k == N_("selected_audio_stream"))) {
+               } else if (k == "dcp_ab") {
+                       _dcp_ab = (v == "1");
+               } else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) {
                        if (!version) {
                                audio_stream_index = atoi (v.c_str ());
                        } else {
                                _content_audio_stream = audio_stream_factory (v, version);
                        }
-               } else if (k == N_("external_audio")) {
+               } else if (k == "external_audio") {
                        _external_audio.push_back (v);
-               } else if (k == N_("use_content_audio")) {
-                       _use_content_audio = (v == N_("1"));
-               } else if (k == N_("audio_gain")) {
+               } else if (k == "use_content_audio") {
+                       _use_content_audio = (v == "1");
+               } else if (k == "audio_gain") {
                        _audio_gain = atof (v.c_str ());
-               } else if (k == N_("audio_delay")) {
+               } else if (k == "audio_delay") {
                        _audio_delay = atoi (v.c_str ());
-               } else if (k == N_("still_duration")) {
+               } else if (k == "still_duration") {
                        _still_duration = atoi (v.c_str ());
-               } else if (k == N_("selected_subtitle_stream")) {
+               } else if (k == "selected_subtitle_stream") {
                        if (!version) {
                                subtitle_stream_index = atoi (v.c_str ());
                        } else {
                                _subtitle_stream = subtitle_stream_factory (v, version);
                        }
-               } else if (k == N_("with_subtitles")) {
-                       _with_subtitles = (v == N_("1"));
-               } else if (k == N_("subtitle_offset")) {
+               } else if (k == "with_subtitles") {
+                       _with_subtitles = (v == "1");
+               } else if (k == "subtitle_offset") {
                        _subtitle_offset = atoi (v.c_str ());
-               } else if (k == N_("subtitle_scale")) {
+               } else if (k == "subtitle_scale") {
                        _subtitle_scale = atof (v.c_str ());
-               } else if (k == N_("colour_lut")) {
+               } else if (k == "colour_lut") {
                        _colour_lut = atoi (v.c_str ());
-               } else if (k == N_("j2k_bandwidth")) {
+               } else if (k == "j2k_bandwidth") {
                        _j2k_bandwidth = atoi (v.c_str ());
-               } else if (k == N_("dci_date")) {
+               } else if (k == "dci_date") {
                        _dci_date = boost::gregorian::from_undelimited_string (v);
+               } else if (k == "dcp_frame_rate") {
+                       _dcp_frame_rate = atoi (v.c_str ());
                }
 
                _dci_metadata.read (k, v);
                
                /* Cached stuff */
-               if (k == N_("width")) {
+               if (k == "width") {
                        _size.width = atoi (v.c_str ());
-               } else if (k == N_("height")) {
+               } else if (k == "height") {
                        _size.height = atoi (v.c_str ());
-               } else if (k == N_("length")) {
+               } else if (k == "length") {
                        int const vv = atoi (v.c_str ());
                        if (vv) {
                                _length = vv;
                        }
-               } else if (k == N_("dcp_intrinsic_duration")) {
+               } else if (k == "dcp_intrinsic_duration") {
                        int const vv = atoi (v.c_str ());
                        if (vv) {
                                _dcp_intrinsic_duration = vv;
                        }
-               } else if (k == N_("content_digest")) {
+               } else if (k == "content_digest") {
                        _content_digest = v;
-               } else if (k == N_("content_audio_stream") || (!version && k == N_("audio_stream"))) {
+               } else if (k == "content_audio_stream" || (!version && k == "audio_stream")) {
                        _content_audio_streams.push_back (audio_stream_factory (v, version));
-               } else if (k == N_("external_audio_stream")) {
+               } else if (k == "external_audio_stream") {
                        _external_audio_stream = audio_stream_factory (v, version);
-               } else if (k == N_("subtitle_stream")) {
+               } else if (k == "subtitle_stream") {
                        _subtitle_streams.push_back (subtitle_stream_factory (v, version));
-               } else if (k == N_("frames_per_second")) {
-                       _frames_per_second = atof (v.c_str ());
+               } else if (k == "source_frame_rate") {
+                       _source_frame_rate = atof (v.c_str ());
+               } else if (version < 4 && k == "frames_per_second") {
+                       _source_frame_rate = atof (v.c_str ());
+                       /* Fill in what would have been used for DCP frame rate by the older version */
+                       _dcp_frame_rate = best_dcp_frame_rate (_source_frame_rate);
                }
        }
 
@@ -707,7 +719,7 @@ 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 (frames_per_second ());
+       FrameRateConversion frc (source_frame_rate(), dcp_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
@@ -715,8 +727,8 @@ Film::target_audio_sample_rate () const
           skip/repeat doesn't come into effect here.
        */
 
-       if (dfr.change_speed) {
-               t *= _frames_per_second * dfr.factor() / dfr.frames_per_second;
+       if (frc.change_speed) {
+               t *= source_frame_rate() * frc.factor() / dcp_frame_rate();
        }
 
        return rint (t);
@@ -725,7 +737,7 @@ Film::target_audio_sample_rate () const
 int
 Film::still_duration_in_frames () const
 {
-       return still_duration() * frames_per_second();
+       return still_duration() * source_frame_rate();
 }
 
 /** @return a DCI-compliant name for a DCP of this film */
@@ -746,14 +758,14 @@ Film::dci_name (bool if_created_now) const
                fixed_name = fixed_name.substr (0, 14);
        }
 
-       d << fixed_name << N_("_");
+       d << fixed_name << "_";
 
        if (dcp_content_type()) {
-               d << dcp_content_type()->dci_name() << N_("_");
+               d << dcp_content_type()->dci_name() << "_";
        }
 
        if (format()) {
-               d << format()->dci_name() << N_("_");
+               d << format()->dci_name() << "_";
        }
 
        DCIMetadata const dm = dci_metadata ();
@@ -761,51 +773,51 @@ Film::dci_name (bool if_created_now) const
        if (!dm.audio_language.empty ()) {
                d << dm.audio_language;
                if (!dm.subtitle_language.empty() && with_subtitles()) {
-                       d << N_("-") << dm.subtitle_language;
+                       d << "-" << dm.subtitle_language;
                } else {
-                       d << N_("-XX");
+                       d << "-XX";
                }
                        
-               d << N_("_");
+               d << "_";
        }
 
        if (!dm.territory.empty ()) {
                d << dm.territory;
                if (!dm.rating.empty ()) {
-                       d << N_("-") << dm.rating;
+                       d << "-" << dm.rating;
                }
-               d << N_("_");
+               d << "_";
        }
 
        switch (audio_channels()) {
        case 1:
-               d << N_("10_");
+               d << "10_";
                break;
        case 2:
-               d << N_("20_");
+               d << "20_";
                break;
        case 6:
-               d << N_("51_");
+               d << "51_";
                break;
        case 8:
-               d << N_("71_");
+               d << "71_";
                break;
        }
 
-       d << N_("2K_");
+       d << "2K_";
 
        if (!dm.studio.empty ()) {
-               d << dm.studio << N_("_");
+               d << dm.studio << "_";
        }
 
        if (if_created_now) {
-               d << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ()) << N_("_");
+               d << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ()) << "_";
        } else {
-               d << boost::gregorian::to_iso_string (_dci_date) << N_("_");
+               d << boost::gregorian::to_iso_string (_dci_date) << "_";
        }
 
        if (!dm.facility.empty ()) {
-               d << dm.facility << N_("_");
+               d << dm.facility << "_";
        }
 
        if (!dm.package_type.empty ()) {
@@ -860,7 +872,7 @@ Film::set_content (string c)
 {
        string check = directory ();
 
-       boost::filesystem::path slash (N_("/"));
+       boost::filesystem::path slash ("/");
        string platform_slash = slash.make_preferred().string ();
 
        if (!ends_with (check, platform_slash)) {
@@ -898,7 +910,8 @@ Film::set_content (string c)
                Decoders d = decoder_factory (shared_from_this(), DecodeOptions());
                
                set_size (d.video->native_size ());
-               set_frames_per_second (d.video->frames_per_second ());
+               set_source_frame_rate (d.video->frames_per_second ());
+               set_dcp_frame_rate (best_dcp_frame_rate (source_frame_rate ()));
                set_subtitle_streams (d.video->subtitle_streams ());
                if (d.audio) {
                        set_content_audio_streams (d.audio->audio_streams ());
@@ -933,10 +946,10 @@ Film::set_content (string c)
        /* Default format */
        switch (content_type()) {
        case STILL:
-               set_format (Format::from_id (N_("var-185")));
+               set_format (Format::from_id ("var-185"));
                break;
        case VIDEO:
-               set_format (Format::from_id (N_("185")));
+               set_format (Format::from_id ("185"));
                break;
        }
 
@@ -1236,6 +1249,17 @@ Film::set_dci_metadata (DCIMetadata m)
        signal_changed (DCI_METADATA);
 }
 
+
+void
+Film::set_dcp_frame_rate (int f)
+{
+       {
+               boost::mutex::scoped_lock lm (_state_mutex);
+               _dcp_frame_rate = f;
+       }
+       signal_changed (DCP_FRAME_RATE);
+}
+
 void
 Film::set_size (libdcp::Size s)
 {
@@ -1307,13 +1331,13 @@ Film::set_subtitle_streams (vector<shared_ptr<SubtitleStream> > s)
 }
 
 void
-Film::set_frames_per_second (float f)
+Film::set_source_frame_rate (float f)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _frames_per_second = f;
+               _source_frame_rate = f;
        }
-       signal_changed (FRAMES_PER_SECOND);
+       signal_changed (SOURCE_FRAME_RATE);
 }
        
 void
@@ -1364,7 +1388,7 @@ Film::info_path (int f) const
 
        stringstream s;
        s.width (8);
-       s << setfill('0') << f << N_(".md5");
+       s << setfill('0') << f << ".md5";
 
        p /= s.str();
 
@@ -1378,15 +1402,15 @@ string
 Film::j2c_path (int f, bool t) const
 {
        boost::filesystem::path p;
-       p /= N_("j2c");
+       p /= "j2c";
        p /= video_state_identifier ();
 
        stringstream s;
        s.width (8);
-       s << setfill('0') << f << N_(".j2c");
+       s << setfill('0') << f << ".j2c";
 
        if (t) {
-               s << N_(".tmp");
+               s << ".tmp";
        }
 
        p /= s.str();
index 847ab434ea06adfc98a169626da7e7b0b5f5fa78..9921acbb45415835f87db6fcfe1f3f03a58ac6a0 100644 (file)
@@ -148,7 +148,8 @@ public:
                DCP_INTRINSIC_DURATION,
                CONTENT_AUDIO_STREAMS,
                SUBTITLE_STREAMS,
-               FRAMES_PER_SECOND,
+               SOURCE_FRAME_RATE,
+               DCP_FRAME_RATE
        };
 
 
@@ -285,6 +286,11 @@ public:
                boost::mutex::scoped_lock lm (_state_mutex);
                return _dci_metadata;
        }
+
+       int dcp_frame_rate () const {
+               boost::mutex::scoped_lock lm (_state_mutex);
+               return _dcp_frame_rate;
+       }
        
        libdcp::Size size () const {
                boost::mutex::scoped_lock lm (_state_mutex);
@@ -311,13 +317,13 @@ public:
                return _subtitle_streams;
        }
        
-       float frames_per_second () const {
+       float source_frame_rate () const {
                boost::mutex::scoped_lock lm (_state_mutex);
                if (content_type() == STILL) {
                        return 24;
                }
                
-               return _frames_per_second;
+               return _source_frame_rate;
        }
 
        boost::shared_ptr<AudioStream> audio_stream () const;
@@ -355,6 +361,7 @@ public:
        void set_colour_lut (int);
        void set_j2k_bandwidth (int);
        void set_dci_metadata (DCIMetadata);
+       void set_dcp_frame_rate (int);
        void set_size (libdcp::Size);
        void set_length (SourceFrame);
        void unset_length ();
@@ -362,7 +369,7 @@ public:
        void set_content_digest (std::string);
        void set_content_audio_streams (std::vector<boost::shared_ptr<AudioStream> >);
        void set_subtitle_streams (std::vector<boost::shared_ptr<SubtitleStream> >);
-       void set_frames_per_second (float);
+       void set_source_frame_rate (float);
 
        /** Emitted when some property has changed */
        mutable boost::signals2::signal<void (Property)> Changed;
@@ -461,6 +468,8 @@ private:
        DCIMetadata _dci_metadata;
        /** The date that we should use in a DCI name */
        boost::gregorian::date _dci_date;
+       /** Frames per second to run our DCP at */
+       int _dcp_frame_rate;
 
        /* Data which are cached to speed things up */
 
@@ -478,7 +487,7 @@ private:
        /** the subtitle streams that we can use */
        std::vector<boost::shared_ptr<SubtitleStream> > _subtitle_streams;
        /** Frames per second of the source */
-       float _frames_per_second;
+       float _source_frame_rate;
 
        /** true if our state has changed since we last saved it */
        mutable bool _dirty;
index 7fb73d0f9264580653b2d89e67795a19dbd37333..b506c70002e82405f569624f0bbd9806b11c3675 100644 (file)
@@ -71,19 +71,70 @@ void
 Format::setup_formats ()
 {
        /// TRANSLATORS: these are film picture aspect ratios; "Academy" means 1.37, "Flat" 1.85 and "Scope" 2.39.
-       _formats.push_back (new FixedFormat (119, libdcp::Size (1285, 1080), N_("119"), _("1.19"), N_("F")));
-       _formats.push_back (new FixedFormat (133, libdcp::Size (1436, 1080), N_("133"), _("1.33"), N_("F")));
-       _formats.push_back (new FixedFormat (138, libdcp::Size (1485, 1080), N_("138"), _("1.375"), N_("F")));
-       _formats.push_back (new FixedFormat (133, libdcp::Size (1998, 1080), N_("133-in-flat"), _("4:3 within Flat"), N_("F")));
-       _formats.push_back (new FixedFormat (137, libdcp::Size (1480, 1080), N_("137"), _("Academy"), N_("F")));
-       _formats.push_back (new FixedFormat (166, libdcp::Size (1793, 1080), N_("166"), _("1.66"), N_("F")));
-       _formats.push_back (new FixedFormat (166, libdcp::Size (1998, 1080), N_("166-in-flat"), _("1.66 within Flat"), N_("F")));
-       _formats.push_back (new FixedFormat (178, libdcp::Size (1998, 1080), N_("178-in-flat"), _("16:9 within Flat"), N_("F")));
-       _formats.push_back (new FixedFormat (178, libdcp::Size (1920, 1080), N_("178"), _("16:9"), N_("F")));
-       _formats.push_back (new FixedFormat (185, libdcp::Size (1998, 1080), N_("185"), _("Flat"), N_("F")));
-       _formats.push_back (new FixedFormat (239, libdcp::Size (2048, 858), N_("239"), _("Scope"), N_("S")));
-       _formats.push_back (new VariableFormat (libdcp::Size (1998, 1080), N_("var-185"), _("Flat without stretch"), N_("F")));
-       _formats.push_back (new VariableFormat (libdcp::Size (2048, 858), N_("var-239"), _("Scope without stretch"), N_("S")));
+       _formats.push_back (
+               new FixedFormat (119, libdcp::Size (1285, 1080), N_("119"), _("1.19"), N_("F"),
+                                _("Source scaled to 1.19:1")
+                       ));
+       
+       _formats.push_back (
+               new FixedFormat (133, libdcp::Size (1436, 1080), N_("133"), _("1.33"), N_("F"),
+                                _("Source scaled to 1.33:1")
+                       ));
+       
+       _formats.push_back (
+               new FixedFormat (138, libdcp::Size (1485, 1080), N_("138"), _("1.375"), N_("F"),
+                                _("Source scaled to 1.375:1")
+                       ));
+       
+       _formats.push_back (
+               new FixedFormat (133, libdcp::Size (1998, 1080), N_("133-in-flat"), _("4:3 within Flat"), N_("F"),
+                                _("Source scaled to 1.33:1 then pillarboxed to Flat")
+                       ));
+       
+       _formats.push_back (
+               new FixedFormat (137, libdcp::Size (1480, 1080), N_("137"), _("Academy"), N_("F"),
+                                _("Source scaled to 1.37:1 (Academy ratio)")
+                       ));
+       
+       _formats.push_back (
+               new FixedFormat (166, libdcp::Size (1793, 1080), N_("166"), _("1.66"), N_("F"),
+                                _("Source scaled to 1.66:1")
+                       ));
+       
+       _formats.push_back (
+               new FixedFormat (166, libdcp::Size (1998, 1080), N_("166-in-flat"), _("1.66 within Flat"), N_("F"),
+                                _("Source scaled to 1.66:1 then pillarboxed to Flat")
+                       ));
+       
+       _formats.push_back (
+               new FixedFormat (178, libdcp::Size (1998, 1080), N_("178-in-flat"), _("16:9 within Flat"), N_("F"),
+                                _("Source scaled to 1.78:1 then pillarboxed to Flat")
+                       ));
+       
+       _formats.push_back (
+               new FixedFormat (178, libdcp::Size (1920, 1080), N_("178"), _("16:9"), N_("F"),
+                                _("Source scaled to 1.78:1")
+                       ));
+       
+       _formats.push_back (
+               new FixedFormat (185, libdcp::Size (1998, 1080), N_("185"), _("Flat"), N_("F"),
+                                _("Source scaled to Flat (1.85:1)")
+                       ));
+       
+       _formats.push_back (
+               new FixedFormat (239, libdcp::Size (2048, 858), N_("239"), _("Scope"), N_("S"),
+                                _("Source scaled to Scope (2.39:1)")
+                       ));
+               
+       _formats.push_back (
+               new VariableFormat (libdcp::Size (1998, 1080), N_("var-185"), _("Flat without stretch"), N_("F"),
+                                   _("Source scaled to fit Flat preserving its aspect ratio")
+                       ));
+       
+       _formats.push_back (
+               new VariableFormat (libdcp::Size (2048, 858), N_("var-239"), _("Scope without stretch"), N_("S"),
+                                   _("Source scaled to fit Scope preserving its aspect ratio")
+                       ));
 }
 
 /** @param n Nickname.
@@ -144,8 +195,8 @@ Format::all ()
  *  @param id ID (e.g. 185)
  *  @param n Nick name (e.g. Flat)
  */
-FixedFormat::FixedFormat (int r, libdcp::Size dcp, string id, string n, string d)
-       : Format (dcp, id, n, d)
+FixedFormat::FixedFormat (int r, libdcp::Size dcp, string id, string n, string d, string e)
+       : Format (dcp, id, n, d, e)
        , _ratio (r)
 {
 
@@ -173,8 +224,8 @@ Format::container_ratio_as_float () const
        return static_cast<float> (_dcp_size.width) / _dcp_size.height;
 }
 
-VariableFormat::VariableFormat (libdcp::Size dcp, string id, string n, string d)
-       : Format (dcp, id, n, d)
+VariableFormat::VariableFormat (libdcp::Size dcp, string id, string n, string d, string e)
+       : Format (dcp, id, n, d, e)
 {
 
 }
index 783ff25ce455b31dd207fa4f37026d8fee132a1d..305524628c50f848b4963292a5fb66addabaf8f6 100644 (file)
@@ -31,11 +31,12 @@ class Film;
 class Format
 {
 public:
-       Format (libdcp::Size dcp, std::string id, std::string n, std::string d)
+       Format (libdcp::Size dcp, std::string id, std::string n, std::string d, std::string e)
                : _dcp_size (dcp)
                , _id (id)
                , _nickname (n)
                , _dci_name (d)
+               , _description (e)
        {}
 
        /** @return the aspect ratio multiplied by 100
@@ -75,6 +76,10 @@ public:
                return _dci_name;
        }
 
+       std::string description () const {
+               return _description;
+       }
+
        std::string as_metadata () const;
 
        static Format const * from_nickname (std::string n);
@@ -94,6 +99,7 @@ protected:
        /** nickname (e.g. Flat, Scope) */
        std::string _nickname;
        std::string _dci_name;
+       std::string _description;
 
 private:       
        /** all available formats */
@@ -107,7 +113,7 @@ private:
 class FixedFormat : public Format
 {
 public:
-       FixedFormat (int, libdcp::Size, std::string, std::string, std::string);
+       FixedFormat (int, libdcp::Size, std::string, std::string, std::string, std::string);
 
        int ratio_as_integer (boost::shared_ptr<const Film>) const {
                return _ratio;
@@ -128,7 +134,7 @@ private:
 class VariableFormat : public Format
 {
 public:
-       VariableFormat (libdcp::Size, std::string, std::string, std::string);
+       VariableFormat (libdcp::Size, std::string, std::string, std::string, std::string);
 
        int ratio_as_integer (boost::shared_ptr<const Film> f) const;
        float ratio_as_float (boost::shared_ptr<const Film> f) const;
index 61fad2e2b3e8df7d0cc137f14ba26fb7c105faea..f7cc500fe2fead1ef78cef63ff353abf67ac62ef 100644 (file)
@@ -120,8 +120,8 @@ TranscodeJob::remaining_time () const
 
        /* Compute approximate proposed length here, as it's only here that we need it */
        int length = _film->length().get();
-       DCPFrameRate const dfr (_film->frames_per_second ());
-       if (dfr.skip) {
+       FrameRateConversion const frc (_film->source_frame_rate(), _film->dcp_frame_rate());
+       if (frc.skip) {
                length /= 2;
        }
        /* If we are repeating it shouldn't affect transcode time, so don't take it into account */
index 959fac8572f55d5593fdd04b0f878bb99200c54f..9720ca56ad3654849a31fa258292a60d75f139b3 100644 (file)
@@ -57,7 +57,7 @@ Transcoder::Transcoder (shared_ptr<Film> f, DecodeOptions o, Job* j, shared_ptr<
 
        if (f->audio_stream()) {
                shared_ptr<AudioStream> st = f->audio_stream();
-               _matcher.reset (new Matcher (f->log(), st->sample_rate(), f->frames_per_second()));
+               _matcher.reset (new Matcher (f->log(), st->sample_rate(), f->source_frame_rate()));
                _delay_line.reset (new DelayLine (f->log(), st->channels(), f->audio_delay() * st->sample_rate() / 1000));
                _gain.reset (new Gain (f->log(), f->audio_gain()));
        }
index de69636da7803e6359eb8e9fd0614eb703d7b003..71e864ebdc387545a93761581769cb7b7bb92b67 100644 (file)
@@ -375,21 +375,12 @@ public:
                , 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)
+int
+best_dcp_frame_rate (float source_fps)
 {
        list<int> const allowed_dcp_frame_rates = Config::instance()->allowed_dcp_frame_rates ();
 
@@ -427,14 +418,8 @@ DCPFrameRate::DCPFrameRate (float source_fps)
                ++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);
+       assert (best);
+       return best->dcp;
 }
 
 /** @param An arbitrary sampling rate.
@@ -962,3 +947,32 @@ AudioMapping::dcp_channels () const
 
        return _source_channels;
 }
+
+FrameRateConversion::FrameRateConversion (float source, int dcp)
+       : skip (false)
+       , repeat (false)
+       , change_speed (false)
+{
+       if (fabs (source / 2.0 - dcp) < (fabs (source - dcp))) {
+               skip = true;
+       } else if (fabs (source * 2 - dcp) < fabs (source - dcp)) {
+               repeat = true;
+       }
+
+       change_speed = !about_equal (source * factor(), dcp);
+
+       if (!skip && !repeat && !change_speed) {
+               description = _("DCP and source have the same rate.\n");
+       } else {
+               if (skip) {
+                       description = _("DCP will use every other frame of the source.\n");
+               } else if (repeat) {
+                       description = _("Each source frame will be doubled in the DCP.\n");
+               }
+
+               if (change_speed) {
+                       float const pc = (source * factor()) * 100 / dcp;
+                       description += String::compose (_("DCP will run at %1%% of the source speed."), pc);
+               }
+       }
+}
index 22c6ea95baafa4f1f2cb545c5f20468b2b5e5b80..ec67469c16ebb7fddc9c7731c4cc1f3a07e8f61a 100644 (file)
@@ -62,9 +62,9 @@ extern std::string audio_channel_name (int);
 
 typedef int SourceFrame;
 
-struct DCPFrameRate
+struct FrameRateConversion
 {
-       DCPFrameRate (float);
+       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.
@@ -78,15 +78,13 @@ struct DCPFrameRate
 
                return 1;
        }
-       
-       /** frames per second for the DCP */
-       int frames_per_second;
+
        /** 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 or slower than the source
-        *  without taking into account `repeat'.
+        *  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
@@ -95,8 +93,12 @@ struct DCPFrameRate
         *          source is 12.50fps, DCP is 25fps)
         */
        bool change_speed;
+
+       std::string description;
 };
 
+int best_dcp_frame_rate (float);
+
 enum ContentType {
        STILL, ///< content is still images
        VIDEO  ///< content is a video
index 334ecec6581ce60c060a5e8154c2f65502fbf1c5..5a2f7c9a946e75031edd5c5acda85b7fb397f525 100644 (file)
@@ -67,8 +67,8 @@ Writer::Writer (shared_ptr<Film> f)
                new libdcp::MonoPictureAsset (
                        _film->video_mxf_dir (),
                        _film->video_mxf_filename (),
-                       DCPFrameRate (_film->frames_per_second()).frames_per_second,
-                       _film->format()->dcp_size()
+                       _film->dcp_frame_rate (),
+                       _film->format()->dcp_size ()
                        )
                );
 
@@ -81,7 +81,7 @@ Writer::Writer (shared_ptr<Film> f)
                        new libdcp::SoundAsset (
                                _film->dir (_film->dcp_name()),
                                N_("audio.mxf"),
-                               DCPFrameRate (_film->frames_per_second()).frames_per_second,
+                               _film->dcp_frame_rate (),
                                m.dcp_channels (),
                                dcp_audio_sample_rate (_film->audio_stream()->sample_rate())
                                )
@@ -289,10 +289,15 @@ Writer::finish ()
        }
        
        libdcp::DCP dcp (_film->dir (_film->dcp_name()));
-       DCPFrameRate dfr (_film->frames_per_second ());
 
        shared_ptr<libdcp::CPL> cpl (
-               new libdcp::CPL (_film->dir (_film->dcp_name()), _film->dcp_name(), _film->dcp_content_type()->libdcp_kind (), frames, dfr.frames_per_second)
+               new libdcp::CPL (
+                       _film->dir (_film->dcp_name()),
+                       _film->dcp_name(),
+                       _film->dcp_content_type()->libdcp_kind (),
+                       frames,
+                       _film->dcp_frame_rate ()
+                       )
                );
        
        dcp.add_cpl (cpl);
index af8460a1653ac7a74d9478b7c1d809d0a8462343..cf44eb69fa59c84c93011619c99718b5a0f27b2e 100644 (file)
@@ -115,7 +115,7 @@ AudioPlot::paint (wxPaintEvent &)
        wxDouble db_label_height;
        wxDouble db_label_descent;
        wxDouble db_label_leading;
-       gc->GetTextExtent ("-80dB", &_db_label_width, &db_label_height, &db_label_descent, &db_label_leading);
+       gc->GetTextExtent (wxT ("-80dB"), &_db_label_width, &db_label_height, &db_label_descent, &db_label_leading);
 
        _db_label_width += 8;
        
index 24f6c24612a524561945f4a344c5e6a1a42527fd..a19d2fff13ffe369e28bd1e50e8350d26161a840 100644 (file)
@@ -97,50 +97,79 @@ FilmEditor::make_film_panel ()
        _film_sizer = new wxBoxSizer (wxVERTICAL);
        _film_panel->SetSizer (_film_sizer);
 
-       wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
+       wxGridBagSizer* grid = new wxGridBagSizer (4, 4);
        _film_sizer->Add (grid, 0, wxALL, 8);
 
-       add_label_to_sizer (grid, _film_panel, _("Name"));
+       int r = 0;
+       
+       add_label_to_grid_bag_sizer (grid, _film_panel, _("Name"), wxGBPosition (r, 0));
        _name = new wxTextCtrl (_film_panel, wxID_ANY);
-       grid->Add (_name, 1, wxEXPAND);
-
-       add_label_to_sizer (grid, _film_panel, _("DCP Name"));
+       grid->Add (_name, wxGBPosition(r, 1));
+       ++r;
+       
+       add_label_to_grid_bag_sizer (grid, _film_panel, _("DCP Name"), wxGBPosition (r, 0));
        _dcp_name = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
-       grid->Add (_dcp_name, 0, wxALIGN_CENTER_VERTICAL | wxSHRINK);
+       grid->Add (_dcp_name, wxGBPosition(r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+       ++r;
 
        _use_dci_name = new wxCheckBox (_film_panel, wxID_ANY, _("Use DCI name"));
-       grid->Add (_use_dci_name, 1, wxEXPAND);
+       grid->Add (_use_dci_name, wxGBPosition (r, 0));
        _edit_dci_button = new wxButton (_film_panel, wxID_ANY, _("Details..."));
-       grid->Add (_edit_dci_button, 0);
+       grid->Add (_edit_dci_button, wxGBPosition (r, 1), wxDefaultSpan);
+       ++r;
 
-       add_label_to_sizer (grid, _film_panel, _("Content"));
+       add_label_to_grid_bag_sizer (grid, _film_panel, _("Content"), wxGBPosition (r, 0));
        _content = new wxFilePickerCtrl (_film_panel, wxID_ANY, wxT (""), _("Select Content File"), wxT("*.*"));
-       grid->Add (_content, 1, wxEXPAND);
+       grid->Add (_content, wxGBPosition (r, 1), wxDefaultSpan, wxEXPAND);
+       ++r;
 
        _trust_content_header = new wxCheckBox (_film_panel, wxID_ANY, _("Trust content's header"));
        video_control (_trust_content_header);
-       grid->Add (_trust_content_header, 1);
-       grid->AddSpacer (0);
+       grid->Add (_trust_content_header, wxGBPosition (r, 0), wxGBSpan(1, 2));
+       ++r;
 
-       add_label_to_sizer (grid, _film_panel, _("Content Type"));
+       add_label_to_grid_bag_sizer (grid, _film_panel, _("Content Type"), wxGBPosition (r, 0));
        _dcp_content_type = new wxChoice (_film_panel, wxID_ANY);
-       grid->Add (_dcp_content_type);
+       grid->Add (_dcp_content_type, wxGBPosition (r, 1));
+       ++r;
 
-       video_control (add_label_to_sizer (grid, _film_panel, _("Original Frame Rate")));
-       _frames_per_second = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
-       grid->Add (video_control (_frames_per_second), 1, wxALIGN_CENTER_VERTICAL);
+       video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Original Frame Rate"), wxGBPosition (r, 0)));
+       _source_frame_rate = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
+       grid->Add (video_control (_source_frame_rate), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+       ++r;
+
+       {
+               add_label_to_grid_bag_sizer (grid, _film_panel, _("DCP Frame Rate"), wxGBPosition (r, 0));
+               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               _dcp_frame_rate = new wxChoice (_film_panel, wxID_ANY);
+               s->Add (_dcp_frame_rate, 1, wxALIGN_CENTER_VERTICAL);
+               _best_dcp_frame_rate = new wxButton (_film_panel, wxID_ANY, _("Use best"));
+               s->Add (_best_dcp_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxALL | wxEXPAND, 6);
+               grid->Add (s, wxGBPosition (r, 1));
+       }
+       ++r;
+
+       _frame_rate_description = new wxStaticText (_film_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize);
+       grid->Add (video_control (_frame_rate_description), wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6);
+       wxFont font = _frame_rate_description->GetFont();
+       font.SetStyle(wxFONTSTYLE_ITALIC);
+       font.SetPointSize(font.GetPointSize() - 1);
+       _frame_rate_description->SetFont(font);
+       ++r;
        
-       video_control (add_label_to_sizer (grid, _film_panel, _("Original Size")));
+       video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Original Size"), wxGBPosition (r, 0)));
        _original_size = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
-       grid->Add (video_control (_original_size), 1, wxALIGN_CENTER_VERTICAL);
+       grid->Add (video_control (_original_size), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+       ++r;
        
-       video_control (add_label_to_sizer (grid, _film_panel, _("Length")));
+       video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Length"), wxGBPosition (r, 0)));
        _length = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
-       grid->Add (video_control (_length), 1, wxALIGN_CENTER_VERTICAL);
+       grid->Add (video_control (_length), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+       ++r;
 
 
        {
-               video_control (add_label_to_sizer (grid, _film_panel, _("Trim frames")));
+               video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim frames"), wxGBPosition (r, 0)));
                wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                video_control (add_label_to_sizer (s, _film_panel, _("Start")));
                _trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
@@ -149,30 +178,37 @@ FilmEditor::make_film_panel ()
                _trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
                s->Add (video_control (_trim_end));
 
-               grid->Add (s);
+               grid->Add (s, wxGBPosition (r, 1));
        }
+       ++r;
 
        _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, _("A/B"));
        video_control (_dcp_ab);
-       grid->Add (_dcp_ab, 1);
-       grid->AddSpacer (0);
+       grid->Add (_dcp_ab, wxGBPosition (r, 0));
+       ++r;
 
        /* STILL-only stuff */
        {
-               still_control (add_label_to_sizer (grid, _film_panel, _("Duration")));
+               still_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Duration"), wxGBPosition (r, 0)));
                wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                _still_duration = new wxSpinCtrl (_film_panel);
                still_control (_still_duration);
                s->Add (_still_duration, 1, wxEXPAND);
                /// TRANSLATORS: `s' here is an abbreviation for seconds, the unit of time
                still_control (add_label_to_sizer (s, _film_panel, _("s")));
-               grid->Add (s);
+               grid->Add (s, wxGBPosition (r, 1));
        }
+       ++r;
 
        vector<DCPContentType const *> const ct = DCPContentType::all ();
        for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) {
                _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ()));
        }
+
+       list<int> const dfr = Config::instance()->allowed_dcp_frame_rates ();
+       for (list<int>::const_iterator i = dfr.begin(); i != dfr.end(); ++i) {
+               _dcp_frame_rate->Append (std_to_wx (boost::lexical_cast<string> (*i)));
+       }
 }
 
 void
@@ -191,6 +227,8 @@ FilmEditor::connect_to_widgets ()
        _filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_filters_clicked), 0, this);
        _scaler->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this);
        _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this);
+       _dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_frame_rate_changed), 0, this);
+       _best_dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::best_dcp_frame_rate_clicked), 0, this);
        _dcp_ab->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::dcp_ab_toggled), 0, this);
        _still_duration->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this);
        _trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_start_changed), 0, this);
@@ -224,32 +262,46 @@ FilmEditor::make_video_panel ()
        _video_sizer = new wxBoxSizer (wxVERTICAL);
        _video_panel->SetSizer (_video_sizer);
        
-       wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
+       wxGridBagSizer* grid = new wxGridBagSizer (4, 4);
        _video_sizer->Add (grid, 0, wxALL, 8);
 
-       add_label_to_sizer (grid, _video_panel, _("Format"));
+       int r = 0;
+       add_label_to_grid_bag_sizer (grid, _video_panel, _("Format"), wxGBPosition (r, 0));
        _format = new wxChoice (_video_panel, wxID_ANY);
-       grid->Add (_format);
-
-       add_label_to_sizer (grid, _video_panel, _("Left crop"));
+       grid->Add (_format, wxGBPosition (r, 1));
+       ++r;
+
+       _format_description = new wxStaticText (_video_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize);
+       grid->Add (_format_description, wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6);
+       wxFont font = _format_description->GetFont();
+       font.SetStyle(wxFONTSTYLE_ITALIC);
+       font.SetPointSize(font.GetPointSize() - 1);
+       _format_description->SetFont(font);
+       ++r;
+
+       add_label_to_grid_bag_sizer (grid, _video_panel, _("Left crop"), wxGBPosition (r, 0));
        _left_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
-       grid->Add (_left_crop);
+       grid->Add (_left_crop, wxGBPosition (r, 1));
+       ++r;
 
-       add_label_to_sizer (grid, _video_panel, _("Right crop"));
+       add_label_to_grid_bag_sizer (grid, _video_panel, _("Right crop"), wxGBPosition (r, 0));
        _right_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
-       grid->Add (_right_crop);
+       grid->Add (_right_crop, wxGBPosition (r, 1));
+       ++r;
        
-       add_label_to_sizer (grid, _video_panel, _("Top crop"));
+       add_label_to_grid_bag_sizer (grid, _video_panel, _("Top crop"), wxGBPosition (r, 0));
        _top_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
-       grid->Add (_top_crop);
+       grid->Add (_top_crop, wxGBPosition (r, 1));
+       ++r;
        
-       add_label_to_sizer (grid, _video_panel, _("Bottom crop"));
+       add_label_to_grid_bag_sizer (grid, _video_panel, _("Bottom crop"), wxGBPosition (r, 0));
        _bottom_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
-       grid->Add (_bottom_crop);
+       grid->Add (_bottom_crop, wxGBPosition (r, 1));
+       ++r;
 
        /* VIDEO-only stuff */
        {
-               video_control (add_label_to_sizer (grid, _video_panel, _("Filters")));
+               video_control (add_label_to_grid_bag_sizer (grid, _video_panel, _("Filters"), wxGBPosition (r, 0)));
                wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                _filters = new wxStaticText (_video_panel, wxID_ANY, _("None"));
                video_control (_filters);
@@ -257,34 +309,38 @@ FilmEditor::make_video_panel ()
                _filters_button = new wxButton (_video_panel, wxID_ANY, _("Edit..."));
                video_control (_filters_button);
                s->Add (_filters_button, 0);
-               grid->Add (s, 1);
+               grid->Add (s, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
        }
+       ++r;
 
-       video_control (add_label_to_sizer (grid, _video_panel, _("Scaler")));
+       video_control (add_label_to_grid_bag_sizer (grid, _video_panel, _("Scaler"), wxGBPosition (r, 0)));
        _scaler = new wxChoice (_video_panel, wxID_ANY);
-       grid->Add (video_control (_scaler), 1);
+       grid->Add (video_control (_scaler), wxGBPosition (r, 1));
+       ++r;
 
        vector<Scaler const *> const sc = Scaler::all ();
        for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) {
                _scaler->Append (std_to_wx ((*i)->name()));
        }
 
-       add_label_to_sizer (grid, _video_panel, _("Colour look-up table"));
+       add_label_to_grid_bag_sizer (grid, _video_panel, _("Colour look-up table"), wxGBPosition (r, 0));
        _colour_lut = new wxChoice (_video_panel, wxID_ANY);
        for (int i = 0; i < 2; ++i) {
                _colour_lut->Append (std_to_wx (colour_lut_index_to_name (i)));
        }
        _colour_lut->SetSelection (0);
-       grid->Add (_colour_lut, 1, wxEXPAND);
+       grid->Add (_colour_lut, wxGBPosition (r, 1), wxDefaultSpan, wxEXPAND);
+       ++r;
 
        {
-               add_label_to_sizer (grid, _video_panel, _("JPEG2000 bandwidth"));
+               add_label_to_grid_bag_sizer (grid, _video_panel, _("JPEG2000 bandwidth"), wxGBPosition (r, 0));
                wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                _j2k_bandwidth = new wxSpinCtrl (_video_panel, wxID_ANY);
                s->Add (_j2k_bandwidth, 1);
                add_label_to_sizer (s, _video_panel, _("MBps"));
-               grid->Add (s, 1);
+               grid->Add (s, wxGBPosition (r, 1));
        }
+       ++r;
 
        _left_crop->SetRange (0, 1024);
        _top_crop->SetRange (0, 1024);
@@ -520,7 +576,21 @@ FilmEditor::j2k_bandwidth_changed (wxCommandEvent &)
        }
        
        _film->set_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1e6);
-}      
+}
+
+void
+FilmEditor::dcp_frame_rate_changed (wxCommandEvent &)
+{
+       if (!_film) {
+               return;
+       }
+
+       _film->set_dcp_frame_rate (
+               boost::lexical_cast<int> (
+                       wx_to_std (_dcp_frame_rate->GetString (_dcp_frame_rate->GetSelection ()))
+                       )
+               );
+}
 
 
 /** Called when the metadata stored in the Film object has changed;
@@ -572,6 +642,8 @@ FilmEditor::film_changed (Film::Property p)
                        checked_set (_format, n);
                }
                setup_dcp_name ();
+
+               _format_description->SetLabel (std_to_wx (_film->format()->description ()));
                break;
        }
        case Film::CROP:
@@ -596,9 +668,9 @@ FilmEditor::film_changed (Film::Property p)
                checked_set (_name, _film->name());
                setup_dcp_name ();
                break;
-       case Film::FRAMES_PER_SECOND:
-               s << fixed << setprecision(2) << _film->frames_per_second();
-               _frames_per_second->SetLabel (std_to_wx (s.str ()));
+       case Film::SOURCE_FRAME_RATE:
+               s << fixed << setprecision(2) << _film->source_frame_rate();
+               _source_frame_rate->SetLabel (std_to_wx (s.str ()));
                break;
        case Film::SIZE:
                if (_film->size().width == 0 && _film->size().height == 0) {
@@ -609,8 +681,8 @@ FilmEditor::film_changed (Film::Property p)
                }
                break;
        case Film::LENGTH:
-               if (_film->frames_per_second() > 0 && _film->length()) {
-                       s << _film->length().get() << " " << _("frames") << "; " << seconds_to_hms (_film->length().get() / _film->frames_per_second());
+               if (_film->source_frame_rate() > 0 && _film->length()) {
+                       s << _film->length().get() << " " << _("frames") << "; " << seconds_to_hms (_film->length().get() / _film->source_frame_rate());
                } else if (_film->length()) {
                        s << _film->length().get() << " " << _("frames");
                } 
@@ -700,6 +772,17 @@ FilmEditor::film_changed (Film::Property p)
                setup_audio_details ();
                break;
        }
+       case Film::DCP_FRAME_RATE:
+               for (unsigned int i = 0; i < _dcp_frame_rate->GetCount(); ++i) {
+                       if (wx_to_std (_dcp_frame_rate->GetString(i)) == boost::lexical_cast<string> (_film->dcp_frame_rate())) {
+                               if (_dcp_frame_rate->GetSelection() != int(i)) {
+                                       _dcp_frame_rate->SetSelection (i);
+                                       break;
+                               }
+                       }
+               }
+               _frame_rate_description->SetLabel (std_to_wx (FrameRateConversion (_film->source_frame_rate(), _film->dcp_frame_rate()).description));
+               _best_dcp_frame_rate->Enable (best_dcp_frame_rate (_film->source_frame_rate ()) != _film->dcp_frame_rate ());
        }
 }
 
@@ -782,7 +865,8 @@ FilmEditor::set_film (shared_ptr<Film> f)
        film_changed (Film::LENGTH);
        film_changed (Film::CONTENT_AUDIO_STREAMS);
        film_changed (Film::SUBTITLE_STREAMS);
-       film_changed (Film::FRAMES_PER_SECOND);
+       film_changed (Film::SOURCE_FRAME_RATE);
+       film_changed (Film::DCP_FRAME_RATE);
 }
 
 /** Updates the sensitivity of lots of widgets to a given value.
@@ -807,6 +891,7 @@ FilmEditor::set_things_sensitive (bool s)
        _scaler->Enable (s);
        _audio_stream->Enable (s);
        _dcp_content_type->Enable (s);
+       _dcp_frame_rate->Enable (s);
        _trim_start->Enable (s);
        _trim_end->Enable (s);
        _dcp_ab->Enable (s);
@@ -1184,3 +1269,13 @@ FilmEditor::show_audio_clicked (wxCommandEvent &)
        _audio_dialog->Show ();
        _audio_dialog->set_film (_film);
 }
+
+void
+FilmEditor::best_dcp_frame_rate_clicked (wxCommandEvent &)
+{
+       if (!_film) {
+               return;
+       }
+       
+       _film->set_dcp_frame_rate (best_dcp_frame_rate (_film->source_frame_rate ()));
+}
index 2af747bb0447f35e2d4e666626f5f8c4b37173f1..29b453b8b2e729cceba95d568dc23754adc44072 100644 (file)
@@ -82,6 +82,8 @@ private:
        void subtitle_stream_changed (wxCommandEvent &);
        void use_audio_changed (wxCommandEvent &);
        void external_audio_changed (wxCommandEvent &);
+       void dcp_frame_rate_changed (wxCommandEvent &);
+       void best_dcp_frame_rate_clicked (wxCommandEvent &);
 
        /* Handle changes to the model */
        void film_changed (Film::Property);
@@ -121,6 +123,7 @@ private:
        wxButton* _edit_dci_button;
        /** The Film's format */
        wxChoice* _format;
+       wxStaticText* _format_description;
        /** The Film's content file */
        wxFilePickerCtrl* _content;
        wxCheckBox* _trust_content_header;
@@ -157,8 +160,11 @@ private:
        wxSpinCtrl* _j2k_bandwidth;
        /** The Film's DCP content type */
        wxChoice* _dcp_content_type;
-       /** The Film's frames per second */
-       wxStaticText* _frames_per_second;
+       /** The Film's source frame rate */
+       wxStaticText* _source_frame_rate;
+       wxChoice* _dcp_frame_rate;
+       wxButton* _best_dcp_frame_rate;
+       wxStaticText* _frame_rate_description;
        /** The Film's original size */
        wxStaticText* _original_size;
        /** The Film's length */
index 5eba7fd809c1d11e948988eaacc497d8d842ae22..a40a3c78df34b2892b5de52bed3e40aab28f888d 100644 (file)
@@ -188,7 +188,7 @@ FilmViewer::timer (wxTimerEvent &)
        get_frame ();
 
        if (_film->length()) {
-               int const new_slider_position = 4096 * _decoders.video->last_source_time() / (_film->length().get() / _film->frames_per_second());
+               int const new_slider_position = 4096 * _decoders.video->last_source_time() / (_film->length().get() / _film->source_frame_rate());
                if (new_slider_position != _slider->GetValue()) {
                        _slider->SetValue (new_slider_position);
                }
@@ -237,7 +237,7 @@ FilmViewer::slider_moved (wxScrollEvent &)
                return;
        }
        
-       if (_decoders.video->seek (_slider->GetValue() * _film->length().get() / (4096 * _film->frames_per_second()))) {
+       if (_decoders.video->seek (_slider->GetValue() * _film->length().get() / (4096 * _film->source_frame_rate()))) {
                return;
        }
        
@@ -369,7 +369,7 @@ FilmViewer::check_play_state ()
        }
        
        if (_play_button->GetValue()) {
-               _timer.Start (1000 / _film->frames_per_second());
+               _timer.Start (1000 / _film->source_frame_rate());
        } else {
                _timer.Stop ();
        }
index e93d06dbe2d76638ea600a084155827383c4fb51..f4acb6b1a8f2181d7d108c696d49d678cd17d9c3 100644 (file)
@@ -52,7 +52,9 @@ PropertiesDialog::PropertiesDialog (wxWindow* parent, shared_ptr<Film> film)
 
        if (_film->length()) {
                _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->length().get())));
-               double const disk = ((double) _film->j2k_bandwidth() / 8) * _film->length().get() / (_film->frames_per_second () * 1073741824);
+               FrameRateConversion frc (_film->source_frame_rate(), _film->dcp_frame_rate());
+               int const dcp_length = _film->length().get() * frc.factor();
+               double const disk = ((double) _film->j2k_bandwidth() / 8) * dcp_length / (_film->dcp_frame_rate() * 1073741824);
                stringstream s;
                s << fixed << setprecision (1) << disk << _("Gb");
                _disk->SetLabel (std_to_wx (s.str ()));
index bf78ff4d7029c1e3acf38f6a6c8ff28574ea362b..720a058cb771425b67f0828aed0ba7f9fb8deb63 100644 (file)
@@ -43,6 +43,14 @@ add_label_to_sizer (wxSizer* s, wxWindow* p, wxString t, int prop)
        return m;
 }
 
+wxStaticText *
+add_label_to_grid_bag_sizer (wxGridBagSizer* s, wxWindow* p, wxString t, wxGBPosition pos, wxGBSpan span)
+{
+       wxStaticText* m = new wxStaticText (p, wxID_ANY, t);
+       s->Add (m, pos, span, wxALIGN_CENTER_VERTICAL | wxALL, 6);
+       return m;
+}
+
 /** Pop up an error dialogue box.
  *  @param parent Parent.
  *  @param m Message.
index 6cde08a90924dd93977d628d4b94ac1f48c730a5..bff3d7982edf0b74d70d1cb4f6f030b26a9f9d2a 100644 (file)
 */
 
 #include <wx/wx.h>
+#include <wx/gbsizer.h>
 #include <boost/function.hpp>
 #include <boost/thread.hpp>
 
 class wxFilePickerCtrl;
 class wxSpinCtrl;
+class wxGridBagSizer;
 
 /** @file src/wx/wx_util.h
  *  @brief Some utility functions and classes.
@@ -30,6 +32,7 @@ class wxSpinCtrl;
 
 extern void error_dialog (wxWindow *, wxString);
 extern wxStaticText* add_label_to_sizer (wxSizer *, wxWindow *, wxString, int prop = 0);
+extern wxStaticText* add_label_to_grid_bag_sizer (wxGridBagSizer *, wxWindow *, wxString, wxGBPosition, wxGBSpan span = wxDefaultSpan);
 extern std::string wx_to_std (wxString);
 extern wxString std_to_wx (std::string);
 
index ab40dfe8ddb873d80cd863670c188bb815f0b7ad..10702f8a00239db4e92545f3d58245be4ae7c539 100644 (file)
@@ -1,4 +1,4 @@
-version 3
+version 4
 name fred
 use_dci_name 1
 content 
@@ -32,10 +32,11 @@ studio
 facility 
 package_type 
 dci_date 20130211
+dcp_frame_rate 0
 width 0
 height 0
 length 0
 dcp_intrinsic_duration 0
 content_digest 
 external_audio_stream external 0 0
-frames_per_second 0
+source_frame_rate 0
index 8cfc6e4674dc8c539fc90e0bbbcc4a1d5180e484..15c34ca78dc23206f0efaa9f83e512701db202de 100644 (file)
@@ -500,8 +500,8 @@ BOOST_AUTO_TEST_CASE (make_dcp_with_range_test)
        BOOST_CHECK_EQUAL (JobManager::instance()->errors(), false);
 }
 
-/* Test the constructor of DCPFrameRate */
-BOOST_AUTO_TEST_CASE (dcp_frame_rate_test)
+/* Test best_dcp_frame_rate and FrameRateConversion */
+BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test)
 {
        /* Run some tests with a limited range of allowed rates */
        
@@ -511,71 +511,82 @@ BOOST_AUTO_TEST_CASE (dcp_frame_rate_test)
        afr.push_back (30);
        Config::instance()->set_allowed_dcp_frame_rates (afr);
 
-       DCPFrameRate dfr = DCPFrameRate (60);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 30);
-       BOOST_CHECK_EQUAL (dfr.skip, true);
-       BOOST_CHECK_EQUAL (dfr.repeat, false);
-       BOOST_CHECK_EQUAL (dfr.change_speed, false);
+       int best = best_dcp_frame_rate (60);
+       FrameRateConversion frc = FrameRateConversion (60, best);
+       BOOST_CHECK_EQUAL (best, 30);
+       BOOST_CHECK_EQUAL (frc.skip, true);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, false);
        
-       dfr = DCPFrameRate (50);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 25);
-       BOOST_CHECK_EQUAL (dfr.skip, true);
-       BOOST_CHECK_EQUAL (dfr.repeat, false);
-       BOOST_CHECK_EQUAL (dfr.change_speed, false);
-
-       dfr = DCPFrameRate (48);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 24);
-       BOOST_CHECK_EQUAL (dfr.skip, true);
-       BOOST_CHECK_EQUAL (dfr.repeat, false);
-       BOOST_CHECK_EQUAL (dfr.change_speed, false);
+       best = best_dcp_frame_rate (50);
+       frc = FrameRateConversion (50, best);
+       BOOST_CHECK_EQUAL (best, 25);
+       BOOST_CHECK_EQUAL (frc.skip, true);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+       best = best_dcp_frame_rate (48);
+       frc = FrameRateConversion (48, best);
+       BOOST_CHECK_EQUAL (best, 24);
+       BOOST_CHECK_EQUAL (frc.skip, true);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, false);
        
-       dfr = DCPFrameRate (30);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 30);
-       BOOST_CHECK_EQUAL (dfr.repeat, false);
-       BOOST_CHECK_EQUAL (dfr.change_speed, false);
-
-       dfr = DCPFrameRate (29.97);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 30);
-       BOOST_CHECK_EQUAL (dfr.repeat, false);
-       BOOST_CHECK_EQUAL (dfr.change_speed, true);
+       best = best_dcp_frame_rate (30);
+       frc = FrameRateConversion (30, best);
+       BOOST_CHECK_EQUAL (best, 30);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+       best = best_dcp_frame_rate (29.97);
+       frc = FrameRateConversion (29.97, best);
+       BOOST_CHECK_EQUAL (best, 30);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, true);
        
-       dfr = DCPFrameRate (25);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 25);
-       BOOST_CHECK_EQUAL (dfr.repeat, false);
-       BOOST_CHECK_EQUAL (dfr.change_speed, false);
-
-       dfr = DCPFrameRate (24);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 24);
-       BOOST_CHECK_EQUAL (dfr.repeat, false);
-       BOOST_CHECK_EQUAL (dfr.change_speed, false);
-
-       dfr = DCPFrameRate (14.5);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 30);
-       BOOST_CHECK_EQUAL (dfr.repeat, true);
-       BOOST_CHECK_EQUAL (dfr.change_speed, true);
-
-       dfr = DCPFrameRate (12.6);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 25);
-       BOOST_CHECK_EQUAL (dfr.repeat, true);
-       BOOST_CHECK_EQUAL (dfr.change_speed, true);
-
-       dfr = DCPFrameRate (12.4);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 25);
-       BOOST_CHECK_EQUAL (dfr.repeat, true);
-       BOOST_CHECK_EQUAL (dfr.change_speed, true);
-
-       dfr = DCPFrameRate (12);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 24);
-       BOOST_CHECK_EQUAL (dfr.repeat, true);
-       BOOST_CHECK_EQUAL (dfr.change_speed, false);
+       best = best_dcp_frame_rate (25);
+       frc = FrameRateConversion (25, best);
+       BOOST_CHECK_EQUAL (best, 25);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+       best = best_dcp_frame_rate (24);
+       frc = FrameRateConversion (24, best);
+       BOOST_CHECK_EQUAL (best, 24);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+       best = best_dcp_frame_rate (14.5);
+       frc = FrameRateConversion (14.5, best);
+       BOOST_CHECK_EQUAL (best, 30);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, true);
+       BOOST_CHECK_EQUAL (frc.change_speed, true);
+
+       best = best_dcp_frame_rate (12.6);
+       frc = FrameRateConversion (12.6, best);
+       BOOST_CHECK_EQUAL (best, 25);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, true);
+       BOOST_CHECK_EQUAL (frc.change_speed, true);
+
+       best = best_dcp_frame_rate (12.4);
+       frc = FrameRateConversion (12.4, best);
+       BOOST_CHECK_EQUAL (best, 25);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, true);
+       BOOST_CHECK_EQUAL (frc.change_speed, true);
+
+       best = best_dcp_frame_rate (12);
+       frc = FrameRateConversion (12, best);
+       BOOST_CHECK_EQUAL (best, 24);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, true);
+       BOOST_CHECK_EQUAL (frc.change_speed, false);
 
        /* Now add some more rates and see if it will use them
           in preference to skip/repeat.
@@ -586,29 +597,59 @@ BOOST_AUTO_TEST_CASE (dcp_frame_rate_test)
        afr.push_back (60);
        Config::instance()->set_allowed_dcp_frame_rates (afr);
 
-       dfr = DCPFrameRate (60);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 60);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.repeat, false);
-       BOOST_CHECK_EQUAL (dfr.change_speed, false);
+       best = best_dcp_frame_rate (60);
+       frc = FrameRateConversion (60, best);
+       BOOST_CHECK_EQUAL (best, 60);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, false);
        
-       dfr = DCPFrameRate (50);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 50);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.repeat, false);
-       BOOST_CHECK_EQUAL (dfr.change_speed, false);
-
-       dfr = DCPFrameRate (48);
-       BOOST_CHECK_EQUAL (dfr.frames_per_second, 48);
-       BOOST_CHECK_EQUAL (dfr.skip, false);
-       BOOST_CHECK_EQUAL (dfr.repeat, false);
-       BOOST_CHECK_EQUAL (dfr.change_speed, false);
+       best = best_dcp_frame_rate (50);
+       frc = FrameRateConversion (50, best);
+       BOOST_CHECK_EQUAL (best, 50);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+       best = best_dcp_frame_rate (48);
+       frc = FrameRateConversion (48, best);
+       BOOST_CHECK_EQUAL (best, 48);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, false);
+
+       /* Check some out-there conversions (not the best) */
+       
+       frc = FrameRateConversion (14.99, 24);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, true);
+       BOOST_CHECK_EQUAL (frc.change_speed, true);
+
+       /* Check some conversions with limited DCP targets */
+
+       afr.clear ();
+       afr.push_back (24);
+       Config::instance()->set_allowed_dcp_frame_rates (afr);
+
+       best = best_dcp_frame_rate (25);
+       frc = FrameRateConversion (25, best);
+       BOOST_CHECK_EQUAL (best, 24);
+       BOOST_CHECK_EQUAL (frc.skip, false);
+       BOOST_CHECK_EQUAL (frc.repeat, false);
+       BOOST_CHECK_EQUAL (frc.change_speed, true);
 }
 
 BOOST_AUTO_TEST_CASE (audio_sampling_rate_test)
 {
+       std::list<int> afr;
+       afr.push_back (24);
+       afr.push_back (25);
+       afr.push_back (30);
+       Config::instance()->set_allowed_dcp_frame_rates (afr);
+
        shared_ptr<Film> f = new_test_film ("audio_sampling_rate_test");
-       f->set_frames_per_second (24);
+       f->set_source_frame_rate (24);
+       f->set_dcp_frame_rate (24);
 
        f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
        BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000);
@@ -619,13 +660,36 @@ BOOST_AUTO_TEST_CASE (audio_sampling_rate_test)
        f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 80000, 0)));
        BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 96000);
 
-       f->set_frames_per_second (23.976);
+       f->set_source_frame_rate (23.976);
+       f->set_dcp_frame_rate (best_dcp_frame_rate (23.976));
        f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
        BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 47952);
 
-       f->set_frames_per_second (29.97);
+       f->set_source_frame_rate (29.97);
+       f->set_dcp_frame_rate (best_dcp_frame_rate (29.97));
+       BOOST_CHECK_EQUAL (f->dcp_frame_rate (), 30);
        f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
        BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 47952);
+
+       f->set_source_frame_rate (25);
+       f->set_dcp_frame_rate (24);
+       f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
+       BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 50000);
+
+       f->set_source_frame_rate (25);
+       f->set_dcp_frame_rate (24);
+       f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
+       BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 50000);
+
+       /* Check some out-there conversions (not the best) */
+       
+       f->set_source_frame_rate (14.99);
+       f->set_dcp_frame_rate (25);
+       f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 16000, 0)));
+       /* The FrameRateConversion within target_audio_sample_rate should choose to double-up
+          the 14.99 fps video to 30 and then run it slow at 25.
+       */
+       BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), rint (48000 * 2 * 14.99 / 25));
 }
 
 class TestJob : public Job