Merge.
authorCarl Hetherington <cth@carlh.net>
Thu, 7 Feb 2013 00:02:15 +0000 (00:02 +0000)
committerCarl Hetherington <cth@carlh.net>
Thu, 7 Feb 2013 00:02:15 +0000 (00:02 +0000)
77 files changed:
.gitignore
ChangeLog
NOTES [new file with mode: 0644]
src/lib/ab_transcode_job.cc
src/lib/ab_transcode_job.h
src/lib/ab_transcoder.cc
src/lib/ab_transcoder.h
src/lib/audio_decoder.cc
src/lib/audio_decoder.h
src/lib/check_hashes_job.cc [deleted file]
src/lib/check_hashes_job.h [deleted file]
src/lib/config.cc
src/lib/config.h
src/lib/dcp_video_frame.cc
src/lib/dcp_video_frame.h
src/lib/decoder.cc
src/lib/decoder.h
src/lib/decoder_factory.cc
src/lib/decoder_factory.h
src/lib/delay_line.cc
src/lib/delay_line.h
src/lib/encoder.cc
src/lib/encoder.h
src/lib/examine_content_job.cc
src/lib/external_audio_decoder.cc
src/lib/external_audio_decoder.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/film.cc
src/lib/film.h
src/lib/filter.cc
src/lib/filter.h
src/lib/filter_graph.cc
src/lib/format.cc
src/lib/image.cc
src/lib/image.h
src/lib/imagemagick_decoder.cc
src/lib/imagemagick_decoder.h
src/lib/log.cc
src/lib/matcher.cc
src/lib/options.h
src/lib/scp_dcp_job.cc
src/lib/server.cc
src/lib/subtitle.cc
src/lib/transcode_job.cc
src/lib/transcode_job.h
src/lib/transcoder.cc
src/lib/transcoder.h
src/lib/util.cc
src/lib/util.h
src/lib/video_decoder.cc
src/lib/video_decoder.h
src/lib/writer.cc [new file with mode: 0644]
src/lib/writer.h [new file with mode: 0644]
src/lib/wscript
src/tools/dvdomatic.cc
src/tools/makedcp.cc
src/wx/config_dialog.cc
src/wx/dci_metadata_dialog.cc
src/wx/dir_picker_ctrl.cc
src/wx/film_editor.cc
src/wx/film_editor.h
src/wx/film_viewer.cc
src/wx/filter_view.cc
src/wx/gain_calculator_dialog.cc
src/wx/job_manager_view.cc
src/wx/job_wrapper.cc
src/wx/new_film_dialog.cc
src/wx/properties_dialog.cc
src/wx/server_dialog.cc
src/wx/wx_util.cc
src/wx/wx_util.h
test/metadata.ref
test/test.cc
windows/installer.nsi.32.in
windows/installer.nsi.64.in
wscript

index 4c0c1926d846114f0346326afb5f784bcf8ecce3..cc3351558baabbdbcfe257501c77ad3c333056cf 100644 (file)
@@ -17,4 +17,7 @@ doc/manual/pdf
 doc/manual/extensions.ent
 .be/id-cache
 *.pyc
-
+GPATH
+GRTAGS
+GSYMS
+GTAGS
index 25a55aa6b4fe1ea7fce3ce378a056a91003a1ce2..fc623d7c50d4b9c7f0768ccf4b628eef5ceca492 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2013-02-02  Carl Hetherington  <cth@carlh.net>
+
+       * Tidy up filters dialog by not showing those
+       that are not configured in FFmpeg, and by splitting
+       them up into categories.
+
+       * Fix infinite loop of error messages when
+       `playing back' using a non-existant filter (#39).
+
+       * Encode data straight to MXFs, rather
+       than going via .j2c files.  Should roughly
+       halve required disk space and reduce time
+       taken.
+
 2013-01-25  Carl Hetherington  <cth@carlh.net>
 
        * When using formats which pad smaller frames into
diff --git a/NOTES b/NOTES
new file mode 100644 (file)
index 0000000..245017e
--- /dev/null
+++ b/NOTES
@@ -0,0 +1,4 @@
+
+... perhaps generate the CPL hash on the fly
+Make check of hashes optional; recovery in general
+Fix multi-reel or remove it
\ No newline at end of file
index a6233c185783d42055be316361ff4e4713c76a3a..0efd277bb73799a76fdbf0f68aec04e36f7ab2d0 100644 (file)
@@ -30,12 +30,11 @@ using std::string;
 using boost::shared_ptr;
 
 /** @param f Film to compare.
- *  @param o Options.
+ *  @param o Decode options.
  */
-ABTranscodeJob::ABTranscodeJob (shared_ptr<Film> f, shared_ptr<const DecodeOptions> od, shared_ptr<const EncodeOptions> oe, shared_ptr<Job> req)
+ABTranscodeJob::ABTranscodeJob (shared_ptr<Film> f, DecodeOptions o, shared_ptr<Job> req)
        : Job (f, req)
-       , _decode_opt (od)
-       , _encode_opt (oe)
+       , _decode_opt (o)
 {
        _film_b.reset (new Film (*_film));
        _film_b->set_scaler (Config::instance()->reference_scaler ());
@@ -53,7 +52,7 @@ ABTranscodeJob::run ()
 {
        try {
                /* _film_b is the one with reference filters */
-               ABTranscoder w (_film_b, _film, _decode_opt, this, shared_ptr<Encoder> (new Encoder (_film, _encode_opt)));
+               ABTranscoder w (_film_b, _film, _decode_opt, this, shared_ptr<Encoder> (new Encoder (_film)));
                w.go ();
                set_progress (1);
                set_state (FINISHED_OK);
index 86a2a81b8b2b0e4416503eb00ae1c7025f5dd5a2..983842038f936cb4ad0f0e63aeef8600e82712aa 100644 (file)
 
 #include <boost/shared_ptr.hpp>
 #include "job.h"
+#include "options.h"
 
 class Film;
-class DecodeOptions;
-class EncodeOptions;
 
 /** @class ABTranscodeJob
  *  @brief Job to run a transcoder which produces output for A/B comparison of various settings.
@@ -40,8 +39,7 @@ class ABTranscodeJob : public Job
 public:
        ABTranscodeJob (
                boost::shared_ptr<Film> f,
-               boost::shared_ptr<const DecodeOptions> od,
-               boost::shared_ptr<const EncodeOptions> oe,
+               DecodeOptions o,
                boost::shared_ptr<Job> req
                );
 
@@ -49,8 +47,7 @@ public:
        void run ();
 
 private:
-       boost::shared_ptr<const DecodeOptions> _decode_opt;
-       boost::shared_ptr<const EncodeOptions> _encode_opt;
+       DecodeOptions _decode_opt;
        
        /** Copy of our Film using the reference filters and scaler */
        boost::shared_ptr<Film> _film_b;
index 53af43b5d91a2be5f259bad4d1d725fb3f18c067..fc4fb8daa44c96105f11ea14a5562bc847344ca9 100644 (file)
@@ -49,7 +49,7 @@ using boost::shared_ptr;
  */
 
 ABTranscoder::ABTranscoder (
-       shared_ptr<Film> a, shared_ptr<Film> b, shared_ptr<const DecodeOptions> o, Job* j, shared_ptr<Encoder> e)
+       shared_ptr<Film> a, shared_ptr<Film> b, DecodeOptions o, Job* j, shared_ptr<Encoder> e)
        : _film_a (a)
        , _film_b (b)
        , _job (j)
index 7bfcb393cb32182d833bde5cc52eeae0e47327a1..58a08af04ca7fbe67eddd78937af60c0fd53b51d 100644 (file)
@@ -31,7 +31,6 @@ class Job;
 class Encoder;
 class VideoDecoder;
 class AudioDecoder;
-class DecodeOptions;
 class Image;
 class Log;
 class Subtitle;
@@ -51,7 +50,7 @@ public:
        ABTranscoder (
                boost::shared_ptr<Film> a,
                boost::shared_ptr<Film> b,
-               boost::shared_ptr<const DecodeOptions> o,
+               DecodeOptions o,
                Job* j,
                boost::shared_ptr<Encoder> e
                );
index 9d8de971c654356a753f85fc48b8618d49568fc9..a038dd2bb22131b8ea4d2888216acd224a0fddc1 100644 (file)
@@ -23,7 +23,7 @@
 using boost::optional;
 using boost::shared_ptr;
 
-AudioDecoder::AudioDecoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j)
+AudioDecoder::AudioDecoder (shared_ptr<Film> f, DecodeOptions o, Job* j)
        : Decoder (f, o, j)
 {
 
index 013a6327f1d8587b3e9d4991f3603f618e995da5..3bf585f4de1f77259de867f218d66fa6fe4fe141 100644 (file)
@@ -34,7 +34,7 @@
 class AudioDecoder : public AudioSource, public virtual Decoder
 {
 public:
-       AudioDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *);
+       AudioDecoder (boost::shared_ptr<Film>, DecodeOptions, Job *);
 
        virtual void set_audio_stream (boost::shared_ptr<AudioStream>);
 
diff --git a/src/lib/check_hashes_job.cc b/src/lib/check_hashes_job.cc
deleted file mode 100644 (file)
index 701584c..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
-    Copyright (C) 2012 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 <fstream>
-#include <boost/lexical_cast.hpp>
-#include <boost/filesystem.hpp>
-#include "check_hashes_job.h"
-#include "options.h"
-#include "log.h"
-#include "job_manager.h"
-#include "ab_transcode_job.h"
-#include "transcode_job.h"
-#include "film.h"
-#include "exceptions.h"
-
-using std::string;
-using std::stringstream;
-using std::ifstream;
-using boost::shared_ptr;
-
-CheckHashesJob::CheckHashesJob (shared_ptr<Film> f, shared_ptr<const DecodeOptions> od, shared_ptr<const EncodeOptions> oe, shared_ptr<Job> req)
-       : Job (f, req)
-       , _decode_opt (od)
-       , _encode_opt (oe)
-       , _bad (0)
-{
-
-}
-
-string
-CheckHashesJob::name () const
-{
-       return String::compose ("Check hashes of %1", _film->name());
-}
-
-void
-CheckHashesJob::run ()
-{
-       _bad = 0;
-
-       if (!_film->dcp_length()) {
-               throw EncodeError ("cannot check hashes of a DCP with unknown length");
-       }
-       
-       SourceFrame const N = _film->dcp_trim_start() + _film->dcp_length().get();
-       DCPFrameRate const dfr = dcp_frame_rate (_film->frames_per_second ());
-       
-       for (SourceFrame i = _film->dcp_trim_start(); i < N; i += dfr.skip) {
-               string const j2k_file = _encode_opt->frame_out_path (i, false);
-               string const hash_file = _encode_opt->hash_out_path (i, false);
-
-               if (!boost::filesystem::exists (j2k_file)) {
-                       _film->log()->log (String::compose ("Frame %1 has a missing J2K file.", i));
-                       boost::filesystem::remove (hash_file);
-                       ++_bad;
-               } else if (!boost::filesystem::exists (hash_file)) {
-                       _film->log()->log (String::compose ("Frame %1 has a missing hash file.", i));
-                       boost::filesystem::remove (j2k_file);
-                       ++_bad;
-               } else {
-                       ifstream ref (hash_file.c_str ());
-                       string hash;
-                       ref >> hash;
-                       if (hash != md5_digest (j2k_file)) {
-                               _film->log()->log (String::compose ("Frame %1 has wrong hash; deleting.", i));
-                               boost::filesystem::remove (j2k_file);
-                               boost::filesystem::remove (hash_file);
-                               ++_bad;
-                       }
-               }
-
-               set_progress (float (i) / N);
-       }
-
-       if (_bad) {
-               shared_ptr<Job> tc;
-
-               if (_film->dcp_ab()) {
-                       tc.reset (new ABTranscodeJob (_film, _decode_opt, _encode_opt, shared_from_this()));
-               } else {
-                       tc.reset (new TranscodeJob (_film, _decode_opt, _encode_opt, shared_from_this()));
-               }
-               
-               JobManager::instance()->add_after (shared_from_this(), tc);
-               JobManager::instance()->add_after (tc, shared_ptr<Job> (new CheckHashesJob (_film, _decode_opt, _encode_opt, tc)));
-       }
-               
-       set_progress (1);
-       set_state (FINISHED_OK);
-}
-
-string
-CheckHashesJob::status () const
-{
-       stringstream s;
-       s << Job::status ();
-       if (overall_progress() > 0) {
-               if (_bad == 0) {
-                       s << "; no bad frames found";
-               } else if (_bad == 1) {
-                       s << "; 1 bad frame found";
-               } else {
-                       s << "; " << _bad << " bad frames found";
-               }
-       }
-       return s.str ();
-}
diff --git a/src/lib/check_hashes_job.h b/src/lib/check_hashes_job.h
deleted file mode 100644 (file)
index c41af9d..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-    Copyright (C) 2012 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 "job.h"
-
-class DecodeOptions;
-class EncodeOptions;
-
-class CheckHashesJob : public Job
-{
-public:
-       CheckHashesJob (
-               boost::shared_ptr<Film> f,
-               boost::shared_ptr<const DecodeOptions> od,
-               boost::shared_ptr<const EncodeOptions> oe,
-               boost::shared_ptr<Job> req
-               );
-
-       std::string name () const;
-       void run ();
-       std::string status () const;
-
-private:
-       boost::shared_ptr<const DecodeOptions> _decode_opt;
-       boost::shared_ptr<const EncodeOptions> _encode_opt;
-       int _bad;
-};
index c4659eecf6d66722c00ca4e5fbb467a1ad01d23a..c165859b01bd25b3d7e1d768c86f8bca71af8c85 100644 (file)
@@ -44,6 +44,10 @@ Config::Config ()
        , _tms_path (".")
        , _sound_processor (SoundProcessor::from_id ("dolby_cp750"))
 {
+       _allowed_dcp_frame_rates.push_back (24);
+       _allowed_dcp_frame_rates.push_back (25);
+       _allowed_dcp_frame_rates.push_back (30);
+       
        ifstream f (file().c_str ());
        string line;
        while (getline (f, line)) {
index 98cbf67e576b3da04bdb648496ce154bc1d81177..fed297ad00f91018029f3b51b214bbf3b46c3bfd 100644 (file)
@@ -95,6 +95,10 @@ public:
                return _sound_processor;
        }
 
+       std::list<int> allowed_dcp_frame_rates () const {
+               return _allowed_dcp_frame_rates;
+       }
+       
        DCIMetadata default_dci_metadata () const {
                return _default_dci_metadata;
        }
@@ -146,6 +150,10 @@ public:
                _tms_password = p;
        }
 
+       void set_allowed_dcp_frame_rates (std::list<int> const & r) {
+               _allowed_dcp_frame_rates = r;
+       }
+
        void set_default_dci_metadata (DCIMetadata d) {
                _default_dci_metadata = d;
        }
@@ -181,6 +189,7 @@ private:
        std::string _tms_password;
        /** Our sound processor */
        SoundProcessor const * _sound_processor;
+       std::list<int> _allowed_dcp_frame_rates;
        /** Default DCI metadata for newly-created Films */
        DCIMetadata _default_dci_metadata;
 
index 921a1876b5cf560fe4e95c63f6a48c6442a40299..4f3fda44ab759f6645f72f4385bd844a3fb5bde9 100644 (file)
 using std::string;
 using std::stringstream;
 using std::ofstream;
+using std::cout;
 using boost::shared_ptr;
+using libdcp::Size;
 
 /** Construct a DCP video frame.
  *  @param input Input image.
  *  @param out Required size of output, in pixels (including any padding).
  *  @param s Scaler to use.
  *  @param p Number of pixels of padding either side of the image.
- *  @param f Index of the frame within the Film.
- *  @param fps Frames per second of the Film.
+ *  @param f Index of the frame within the DCP's intrinsic duration.
+ *  @param fps Frames per second of the Film's source.
  *  @param pp FFmpeg post-processing string to use.
  *  @param clut Colour look-up table to use (see Config::colour_lut_index ())
  *  @param bw J2K bandwidth to use (see Config::j2k_bandwidth ())
@@ -75,8 +77,8 @@ using boost::shared_ptr;
  */
 DCPVideoFrame::DCPVideoFrame (
        shared_ptr<const Image> yuv, shared_ptr<Subtitle> sub,
-       libdcp::Size out, int p, int subtitle_offset, float subtitle_scale,
-       Scaler const * s, SourceFrame f, float fps, string pp, int clut, int bw, Log* l
+       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
        )
        : _input (yuv)
        , _subtitle (sub)
@@ -86,7 +88,7 @@ DCPVideoFrame::DCPVideoFrame (
        , _subtitle_scale (subtitle_scale)
        , _scaler (s)
        , _frame (f)
-       , _frames_per_second (dcp_frame_rate(fps).frames_per_second)
+       , _frames_per_second (DCPFrameRate(fps).frames_per_second)
        , _post_process (pp)
        , _colour_lut (clut)
        , _j2k_bandwidth (bw)
@@ -371,34 +373,63 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv)
        return e;
 }
 
+EncodedData::EncodedData (int s)
+       : _data (new uint8_t[s])
+       , _size (s)
+{
+
+}
+
+EncodedData::EncodedData (string file)
+{
+       _size = boost::filesystem::file_size (file);
+       _data = new uint8_t[_size];
+
+       FILE* f = fopen (file.c_str(), "rb");
+       if (!f) {
+               throw FileError ("could not open file for reading", file);
+       }
+       
+       fread (_data, 1, _size, f);
+       fclose (f);
+}
+
+
+EncodedData::~EncodedData ()
+{
+       delete[] _data;
+}
+
 /** Write this data to a J2K file.
- *  @param opt Options.
- *  @param frame Frame index.
+ *  @param Film Film.
+ *  @param frame DCP frame index.
  */
 void
-EncodedData::write (shared_ptr<const EncodeOptions> opt, SourceFrame frame)
+EncodedData::write (shared_ptr<const Film> film, int frame) const
 {
-       string const tmp_j2k = opt->frame_out_path (frame, true);
+       string const tmp_j2c = film->j2c_path (frame, true);
 
-       FILE* f = fopen (tmp_j2k.c_str (), "wb");
+       FILE* f = fopen (tmp_j2c.c_str (), "wb");
        
        if (!f) {
-               throw WriteFileError (tmp_j2k, errno);
+               throw WriteFileError (tmp_j2c, errno);
        }
 
        fwrite (_data, 1, _size, f);
        fclose (f);
 
-       string const real_j2k = opt->frame_out_path (frame, false);
+       string const real_j2c = film->j2c_path (frame, false);
 
        /* Rename the file from foo.j2c.tmp to foo.j2c now that it is complete */
-       boost::filesystem::rename (tmp_j2k, real_j2k);
+       boost::filesystem::rename (tmp_j2c, real_j2c);
+}
 
-       /* Write a file containing the hash */
-       string const hash = opt->hash_out_path (frame, false);
-       ofstream h (hash.c_str());
-       h << md5_digest (_data, _size) << "\n";
-       h.close ();
+void
+EncodedData::write_info (shared_ptr<const Film> film, int frame, libdcp::FrameInfo fin) const
+{
+       string const info = film->info_path (frame);
+       ofstream h (info.c_str());
+       fin.write (h);
 }
 
 /** Send this data to a socket.
@@ -413,14 +444,15 @@ EncodedData::send (shared_ptr<Socket> socket)
        socket->write (_data, _size, 30);
 }
 
-/** @param s Size of data in bytes */
-RemotelyEncodedData::RemotelyEncodedData (int s)
-       : EncodedData (new uint8_t[s], s)
+LocallyEncodedData::LocallyEncodedData (uint8_t* d, int s)
+       : EncodedData (s)
 {
-
+       memcpy (_data, d, s);
 }
 
-RemotelyEncodedData::~RemotelyEncodedData ()
+/** @param s Size of data in bytes */
+RemotelyEncodedData::RemotelyEncodedData (int s)
+       : EncodedData (s)
 {
-       delete[] _data;
+
 }
index c0eff3f358a2581565266c4b37695577e1831a76..be8a559b2464d5da6101d0e5cbc11cebb1bd9581 100644 (file)
@@ -19,6 +19,7 @@
 */
 
 #include <openjpeg.h>
+#include <libdcp/picture_asset.h>
 #include "util.h"
 
 /** @file  src/dcp_video_frame.h
@@ -26,7 +27,7 @@
  */
 
 class FilmState;
-class EncodeOptions;
+class Film;
 class ServerDescription;
 class Scaler;
 class Image;
@@ -39,18 +40,16 @@ class Subtitle;
 class EncodedData
 {
 public:
-       /** @param d Data (will not be freed by this class, but may be by subclasses)
-        *  @param s libdcp::Size of data, in bytes.
-        */
-       EncodedData (uint8_t* d, int s)
-               : _data (d)
-               , _size (s)
-       {}
+       /** @param s Size of data, in bytes */
+       EncodedData (int s);
+
+       EncodedData (std::string f);
 
-       virtual ~EncodedData () {}
+       virtual ~EncodedData ();
 
        void send (boost::shared_ptr<Socket> socket);
-       void write (boost::shared_ptr<const EncodeOptions>, SourceFrame);
+       void write (boost::shared_ptr<const Film>, int) const;
+       void write_info (boost::shared_ptr<const Film>, int, libdcp::FrameInfo) const;
 
        /** @return data */
        uint8_t* data () const {
@@ -65,6 +64,10 @@ public:
 protected:
        uint8_t* _data; ///< data
        int _size;      ///< data size in bytes
+
+private:
+       /* No copy construction */
+       EncodedData (EncodedData const &);
 };
 
 /** @class LocallyEncodedData
@@ -75,12 +78,10 @@ protected:
 class LocallyEncodedData : public EncodedData
 {
 public:
-       /** @param d Data (which will not be freed by this class)
-        *  @param s libdcp::Size of data, in bytes.
+       /** @param d Data (which will be copied by this class)
+        *  @param s Size of data, in bytes.
         */
-       LocallyEncodedData (uint8_t* d, int s)
-               : EncodedData (d, s)
-       {}
+       LocallyEncodedData (uint8_t* d, int s);
 };
 
 /** @class RemotelyEncodedData
@@ -91,7 +92,6 @@ class RemotelyEncodedData : public EncodedData
 {
 public:
        RemotelyEncodedData (int s);
-       ~RemotelyEncodedData ();
 };
 
 /** @class DCPVideoFrame
@@ -108,7 +108,7 @@ class DCPVideoFrame
 public:
        DCPVideoFrame (
                boost::shared_ptr<const Image>, boost::shared_ptr<Subtitle>, libdcp::Size,
-               int, int, float, Scaler const *, SourceFrame, float, std::string, int, int, Log *
+               int, int, float, Scaler const *, int, float, std::string, int, int, Log *
                );
        
        virtual ~DCPVideoFrame ();
@@ -116,7 +116,7 @@ public:
        boost::shared_ptr<EncodedData> encode_locally ();
        boost::shared_ptr<EncodedData> encode_remotely (ServerDescription const *);
 
-       SourceFrame frame () const {
+       int frame () const {
                return _frame;
        }
        
@@ -125,12 +125,12 @@ private:
 
        boost::shared_ptr<const Image> _input; ///< the input image
        boost::shared_ptr<Subtitle> _subtitle; ///< any subtitle that should be on the image
-       libdcp::Size _out_size;                  ///< the required size of the output, in pixels
+       libdcp::Size _out_size;                ///< the required size of the output, in pixels
        int _padding;
        int _subtitle_offset;
        float _subtitle_scale;
        Scaler const * _scaler;          ///< scaler to use
-       SourceFrame _frame;              ///< frame index within the Film's source
+       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)
        std::string _post_process;       ///< FFmpeg post-processing string to use
        int _colour_lut;                 ///< Colour look-up table to use
index 7066b488e6507352320c06767caaa216218c0275..fd0abee4183e540b4a4db5c5a9cb3588e5d8a34c 100644 (file)
@@ -46,10 +46,10 @@ using boost::shared_ptr;
 using boost::optional;
 
 /** @param f Film.
- *  @param o Options.
+ *  @param o Decode options.
  *  @param j Job that we are running within, or 0
  */
-Decoder::Decoder (boost::shared_ptr<Film> f, boost::shared_ptr<const DecodeOptions> o, Job* j)
+Decoder::Decoder (boost::shared_ptr<Film> f, DecodeOptions o, Job* j)
        : _film (f)
        , _opt (o)
        , _job (j)
index 3908afa2fbfed522b24266846925fad693ee374f..cc4c87373616e6f4f469e4f3ce9018b4ee5aa929 100644 (file)
@@ -34,9 +34,9 @@
 #include "video_source.h"
 #include "audio_source.h"
 #include "film.h"
+#include "options.h"
 
 class Job;
-class DecodeOptions;
 class Image;
 class Log;
 class DelayLine;
@@ -54,7 +54,7 @@ class FilterGraph;
 class Decoder
 {
 public:
-       Decoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *);
+       Decoder (boost::shared_ptr<Film>, DecodeOptions, Job *);
        virtual ~Decoder () {}
 
        virtual bool pass () = 0;
@@ -66,8 +66,8 @@ public:
 protected:
        /** our Film */
        boost::shared_ptr<Film> _film;
-       /** our options */
-       boost::shared_ptr<const DecodeOptions> _opt;
+       /** our decode options */
+       DecodeOptions _opt;
        /** associated Job, or 0 */
        Job* _job;
 
index 2a0d828e2153d4e418e8f254277f98f719a33754..c4d818f497f6d7eec7f173eea9fa445d9a666f21 100644 (file)
@@ -36,7 +36,7 @@ using boost::dynamic_pointer_cast;
 
 Decoders
 decoder_factory (
-       shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j
+       shared_ptr<Film> f, DecodeOptions o, Job* j
        )
 {
        if (boost::filesystem::is_directory (f->content_path()) || f->content_type() == STILL) {
index 47d977ce7581dd966d7db70980aca574882f0be5..445a1c8a2f7a27462f6532801f51ae4810a7a8a9 100644 (file)
@@ -24,8 +24,9 @@
  *  @brief A method to create appropriate decoders for some content.
  */
 
+#include "options.h"
+
 class Film;
-class DecodeOptions;
 class Job;
 class VideoDecoder;
 class AudioDecoder;
@@ -43,7 +44,7 @@ struct Decoders {
 };
 
 extern Decoders decoder_factory (
-       boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *
+       boost::shared_ptr<Film>, DecodeOptions, Job *
        );
 
 #endif
index 45d8e9d9d36b86f1d79beb9f7db6dca8589e6032..4ad17278169574f6a9af75bd233d7509a80473f3 100644 (file)
@@ -91,12 +91,3 @@ DelayLine::process_audio (shared_ptr<AudioBuffers> data)
 
        Audio (data);
 }
-
-void
-DelayLine::process_end ()
-{
-       if (_frames < 0) {
-               _buffers->make_silent ();
-               Audio (_buffers);
-       }
-}
index fa2870ae76cd4c4252d9c2489804fece215408fe..4d6f1313bd1e5af24fbaf3ed8b61a13312e754cd 100644 (file)
@@ -29,7 +29,6 @@ public:
        DelayLine (Log* log, int channels, int frames);
        
        void process_audio (boost::shared_ptr<AudioBuffers>);
-       void process_end ();
 
 private:
        boost::shared_ptr<AudioBuffers> _buffers;
index 910d7c58e535cba24edd1d54fcc6c05906ba7608..b92be84a8bc8cde04ddfdc738f8bc956eaa14c2a 100644 (file)
@@ -24,6 +24,7 @@
 #include <iostream>
 #include <boost/filesystem.hpp>
 #include <boost/lexical_cast.hpp>
+#include <libdcp/picture_asset.h>
 #include "encoder.h"
 #include "util.h"
 #include "options.h"
@@ -34,7 +35,9 @@
 #include "config.h"
 #include "dcp_video_frame.h"
 #include "server.h"
+#include "format.h"
 #include "cross.h"
+#include "writer.h"
 
 using std::pair;
 using std::string;
@@ -50,41 +53,25 @@ int const Encoder::_history_size = 25;
 /** @param f Film that we are encoding.
  *  @param o Options.
  */
-Encoder::Encoder (shared_ptr<const Film> f, shared_ptr<const EncodeOptions> o)
+Encoder::Encoder (shared_ptr<Film> f)
        : _film (f)
-       , _opt (o)
-       , _just_skipped (false)
-       , _video_frame (0)
-       , _audio_frame (0)
+       , _video_frames_in (0)
+       , _video_frames_out (0)
 #ifdef HAVE_SWRESAMPLE   
        , _swr_context (0)
-#endif   
-       , _audio_frames_written (0)
-       , _process_end (false)
+#endif
+       , _have_a_real_frame (false)
+       , _terminate_encoder (false)
 {
-       if (_film->audio_stream()) {
-               /* Create sound output files with .tmp suffixes; we will rename
-                  them if and when we complete.
-               */
-               for (int i = 0; i < dcp_audio_channels (_film->audio_channels()); ++i) {
-                       SF_INFO sf_info;
-                       sf_info.samplerate = dcp_audio_sample_rate (_film->audio_stream()->sample_rate());
-                       /* We write mono files */
-                       sf_info.channels = 1;
-                       sf_info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24;
-                       SNDFILE* f = sf_open (_opt->multichannel_audio_out_path (i, true).c_str (), SFM_WRITE, &sf_info);
-                       if (f == 0) {
-                               throw CreateFileError (_opt->multichannel_audio_out_path (i, true));
-                       }
-                       _sound_files.push_back (f);
-               }
-       }
+       
 }
 
 Encoder::~Encoder ()
 {
-       close_sound_files ();
        terminate_worker_threads ();
+       if (_writer) {
+               _writer->finish ();
+       }
 }
 
 void
@@ -130,6 +117,8 @@ Encoder::process_begin ()
                        _worker_threads.push_back (new boost::thread (boost::bind (&Encoder::encoder_thread, this, *i)));
                }
        }
+
+       _writer.reset (new Writer (_film));
 }
 
 
@@ -153,32 +142,20 @@ Encoder::process_end ()
                        }
 
                        out->set_frames (frames);
-                       write_audio (out);
+                       _writer->write (out);
                }
 
                swr_free (&_swr_context);
        }
 #endif
 
-       if (_film->audio_stream()) {
-               close_sound_files ();
-               
-               /* Rename .wav.tmp files to .wav */
-               for (int i = 0; i < dcp_audio_channels (_film->audio_channels()); ++i) {
-                       if (boost::filesystem::exists (_opt->multichannel_audio_out_path (i, false))) {
-                               boost::filesystem::remove (_opt->multichannel_audio_out_path (i, false));
-                       }
-                       boost::filesystem::rename (_opt->multichannel_audio_out_path (i, true), _opt->multichannel_audio_out_path (i, false));
-               }
-       }
-
        boost::mutex::scoped_lock lock (_worker_mutex);
 
-       _film->log()->log ("Clearing queue of " + lexical_cast<string> (_queue.size ()));
+       _film->log()->log ("Clearing queue of " + lexical_cast<string> (_encode_queue.size ()));
 
        /* Keep waking workers until the queue is empty */
-       while (!_queue.empty ()) {
-               _film->log()->log ("Waking with " + lexical_cast<string> (_queue.size ()), Log::VERBOSE);
+       while (!_encode_queue.empty ()) {
+               _film->log()->log ("Waking with " + lexical_cast<string> (_encode_queue.size ()), Log::VERBOSE);
                _worker_condition.notify_all ();
                _worker_condition.wait (lock);
        }
@@ -187,7 +164,7 @@ Encoder::process_end ()
        
        terminate_worker_threads ();
 
-       _film->log()->log ("Mopping up " + lexical_cast<string> (_queue.size()));
+       _film->log()->log ("Mopping up " + lexical_cast<string> (_encode_queue.size()));
 
        /* The following sequence of events can occur in the above code:
             1. a remote worker takes the last image off the queue
@@ -198,22 +175,18 @@ Encoder::process_end ()
             So just mop up anything left in the queue here.
        */
 
-       for (list<shared_ptr<DCPVideoFrame> >::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+       for (list<shared_ptr<DCPVideoFrame> >::iterator i = _encode_queue.begin(); i != _encode_queue.end(); ++i) {
                _film->log()->log (String::compose ("Encode left-over frame %1", (*i)->frame ()));
                try {
-                       shared_ptr<EncodedData> e = (*i)->encode_locally ();
-                       e->write (_opt, (*i)->frame ());
+                       _writer->write ((*i)->encode_locally(), (*i)->frame ());
                        frame_done ();
                } catch (std::exception& e) {
                        _film->log()->log (String::compose ("Local encode failed (%1)", e.what ()));
                }
        }
 
-       /* Now do links (or copies on windows) to duplicate frames */
-       for (list<pair<int, int> >::iterator i = _links_required.begin(); i != _links_required.end(); ++i) {
-               link (_opt->frame_out_path (i->first, false), _opt->frame_out_path (i->second, false));
-               link (_opt->hash_out_path (i->first, false), _opt->hash_out_path (i->second, false));
-       }
+       _writer->finish ();
+       _writer.reset ();
 }      
 
 /** @return an estimate of the current number of frames we are encoding per second,
@@ -233,20 +206,12 @@ Encoder::current_frames_per_second () const
        return _history_size / (seconds (now) - seconds (_time_history.back ()));
 }
 
-/** @return true if the last frame to be processed was skipped as it already existed */
-bool
-Encoder::skipping () const
+/** @return Number of video frames that have been sent out */
+int
+Encoder::video_frames_out () const
 {
        boost::mutex::scoped_lock (_history_mutex);
-       return _just_skipped;
-}
-
-/** @return Number of video frames that have been received */
-SourceFrame
-Encoder::video_frame () const
-{
-       boost::mutex::scoped_lock (_history_mutex);
-       return _video_frame;
+       return _video_frames_out;
 }
 
 /** Should be called when a frame has been encoded successfully.
@@ -256,7 +221,6 @@ void
 Encoder::frame_done ()
 {
        boost::mutex::scoped_lock lock (_history_mutex);
-       _just_skipped = false;
        
        struct timeval tv;
        gettimeofday (&tv, 0);
@@ -266,105 +230,67 @@ Encoder::frame_done ()
        }
 }
 
-/** Called by a subclass when it has just skipped the processing
-    of a frame because it has already been done.
-*/
-void
-Encoder::frame_skipped ()
-{
-       boost::mutex::scoped_lock lock (_history_mutex);
-       _just_skipped = true;
-}
-
 void
 Encoder::process_video (shared_ptr<Image> image, bool same, boost::shared_ptr<Subtitle> sub)
 {
-       if (_opt->video_skip != 0 && (_video_frame % _opt->video_skip) != 0) {
-               ++_video_frame;
+       DCPFrameRate dfr (_film->frames_per_second ());
+       
+       if (dfr.skip && (_video_frames_in % 2)) {
+               ++_video_frames_in;
                return;
        }
 
-       if (_opt->video_range) {
-               pair<SourceFrame, SourceFrame> const r = _opt->video_range.get();
-               if (_video_frame < r.first || _video_frame >= r.second) {
-                       ++_video_frame;
-                       return;
-               }
-       }
-
        boost::mutex::scoped_lock lock (_worker_mutex);
 
        /* Wait until the queue has gone down a bit */
-       while (_queue.size() >= _worker_threads.size() * 2 && !_process_end) {
-               TIMING ("decoder sleeps with queue of %1", _queue.size());
+       while (_encode_queue.size() >= _worker_threads.size() * 2 && !_terminate_encoder) {
+               TIMING ("decoder sleeps with queue of %1", _encode_queue.size());
                _worker_condition.wait (lock);
-               TIMING ("decoder wakes with queue of %1", _queue.size());
+               TIMING ("decoder wakes with queue of %1", _encode_queue.size());
        }
 
-       if (_process_end) {
+       if (_terminate_encoder) {
                return;
        }
 
-       /* Only do the processing if we don't already have a file for this frame */
-       if (boost::filesystem::exists (_opt->frame_out_path (_video_frame, false))) {
-               frame_skipped ();
-               return;
-       }
-
-       if (same && _last_real_frame) {
-               /* Use the last frame that we encoded.  We need to postpone doing the actual link,
-                  as on windows the link is really a copy and the reference frame might not have
-                  finished encoding yet.
-               */
-               _links_required.push_back (make_pair (_last_real_frame.get(), _video_frame));
+       if (_writer->can_fake_write (_video_frames_out)) {
+               _writer->fake_write (_video_frames_out);
+               _have_a_real_frame = false;
+       } else if (same && _have_a_real_frame) {
+               /* Use the last frame that we encoded. */
+               _writer->repeat (_video_frames_out);
+               frame_done ();
        } else {
                /* Queue this new frame for encoding */
                pair<string, string> const s = Filter::ffmpeg_strings (_film->filters());
-               TIMING ("adding to queue of %1", _queue.size ());
-               _queue.push_back (boost::shared_ptr<DCPVideoFrame> (
+               TIMING ("adding to queue of %1", _encode_queue.size ());
+               _encode_queue.push_back (boost::shared_ptr<DCPVideoFrame> (
                                          new DCPVideoFrame (
-                                                 image, sub, _opt->out_size, _opt->padding, _film->subtitle_offset(), _film->subtitle_scale(),
-                                                 _film->scaler(), _video_frame, _film->frames_per_second(), s.second,
+                                                 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->colour_lut(), _film->j2k_bandwidth(),
                                                  _film->log()
                                                  )
                                          ));
                
                _worker_condition.notify_all ();
-               _last_real_frame = _video_frame;
+               _have_a_real_frame = true;
        }
 
-       ++_video_frame;
+       ++_video_frames_in;
+       ++_video_frames_out;
+
+       if (dfr.repeat) {
+               _writer->repeat (_video_frames_out);
+               ++_video_frames_out;
+               frame_done ();
+       }
 }
 
 void
 Encoder::process_audio (shared_ptr<AudioBuffers> data)
 {
-       if (_opt->audio_range) {
-               shared_ptr<AudioBuffers> trimmed (new AudioBuffers (*data.get ()));
-               
-               /* Range that we are encoding */
-               pair<int64_t, int64_t> required_range = _opt->audio_range.get();
-               /* Range of this block of data */
-               pair<int64_t, int64_t> this_range (_audio_frame, _audio_frame + trimmed->frames());
-
-               if (this_range.second < required_range.first || required_range.second < this_range.first) {
-                       /* No part of this audio is within the required range */
-                       _audio_frame += data->frames();
-                       return;
-               } else if (required_range.first >= this_range.first && required_range.first < this_range.second) {
-                       /* Trim start */
-                       int64_t const shift = required_range.first - this_range.first;
-                       trimmed->move (shift, 0, trimmed->frames() - shift);
-                       trimmed->set_frames (trimmed->frames() - shift);
-               } else if (required_range.second >= this_range.first && required_range.second < this_range.second) {
-                       /* Trim end */
-                       trimmed->set_frames (required_range.second - this_range.first);
-               }
-
-               data = trimmed;
-       }
-
 #if HAVE_SWRESAMPLE
        /* Maybe sample-rate convert */
        if (_swr_context) {
@@ -406,36 +332,14 @@ Encoder::process_audio (shared_ptr<AudioBuffers> data)
                data = b;
        }
 
-       write_audio (data);
-       
-       _audio_frame += data->frames ();
-}
-
-void
-Encoder::write_audio (shared_ptr<const AudioBuffers> audio)
-{
-       for (int i = 0; i < audio->channels(); ++i) {
-               sf_write_float (_sound_files[i], audio->data(i), audio->frames());
-       }
-
-       _audio_frames_written += audio->frames ();
+       _writer->write (data);
 }
 
-void
-Encoder::close_sound_files ()
-{
-       for (vector<SNDFILE*>::iterator i = _sound_files.begin(); i != _sound_files.end(); ++i) {
-               sf_close (*i);
-       }
-
-       _sound_files.clear ();
-}      
-
 void
 Encoder::terminate_worker_threads ()
 {
        boost::mutex::scoped_lock lock (_worker_mutex);
-       _process_end = true;
+       _terminate_encoder = true;
        _worker_condition.notify_all ();
        lock.unlock ();
 
@@ -458,18 +362,18 @@ Encoder::encoder_thread (ServerDescription* server)
 
                TIMING ("encoder thread %1 sleeps", boost::this_thread::get_id());
                boost::mutex::scoped_lock lock (_worker_mutex);
-               while (_queue.empty () && !_process_end) {
+               while (_encode_queue.empty () && !_terminate_encoder) {
                        _worker_condition.wait (lock);
                }
 
-               if (_process_end) {
+               if (_terminate_encoder) {
                        return;
                }
 
-               TIMING ("encoder thread %1 wakes with queue of %2", boost::this_thread::get_id(), _queue.size());
-               boost::shared_ptr<DCPVideoFrame> vf = _queue.front ();
+               TIMING ("encoder thread %1 wakes with queue of %2", boost::this_thread::get_id(), _encode_queue.size());
+               boost::shared_ptr<DCPVideoFrame> vf = _encode_queue.front ();
                _film->log()->log (String::compose ("Encoder thread %1 pops frame %2 from queue", boost::this_thread::get_id(), vf->frame()), Log::VERBOSE);
-               _queue.pop_front ();
+               _encode_queue.pop_front ();
                
                lock.unlock ();
 
@@ -509,14 +413,14 @@ Encoder::encoder_thread (ServerDescription* server)
                }
 
                if (encoded) {
-                       encoded->write (_opt, vf->frame ());
+                       _writer->write (encoded, vf->frame ());
                        frame_done ();
                } else {
                        lock.lock ();
                        _film->log()->log (
                                String::compose ("Encoder thread %1 pushes frame %2 back onto queue after failure", boost::this_thread::get_id(), vf->frame())
                                );
-                       _queue.push_front (vf);
+                       _encode_queue.push_front (vf);
                        lock.unlock ();
                }
 
@@ -528,18 +432,3 @@ Encoder::encoder_thread (ServerDescription* server)
                _worker_condition.notify_all ();
        }
 }
-
-void
-Encoder::link (string a, string b) const
-{
-#ifdef DVDOMATIC_POSIX                 
-       int const r = symlink (a.c_str(), b.c_str());
-       if (r) {
-               throw EncodeError (String::compose ("could not create symlink from %1 to %2", a, b));
-       }
-#endif
-       
-#ifdef DVDOMATIC_WINDOWS
-       boost::filesystem::copy_file (a, b);
-#endif                 
-}
index 52ccfc166833f60a2e0e9a6efe4583092f15ad54..8b02f70041af349d5e95dbd4a855c15c151a493e 100644 (file)
@@ -39,18 +39,18 @@ extern "C" {
 #include <libswresample/swresample.h>
 }
 #endif
-#include <sndfile.h>
 #include "util.h"
 #include "video_sink.h"
 #include "audio_sink.h"
 
-class EncodeOptions;
 class Image;
 class Subtitle;
 class AudioBuffers;
 class Film;
 class ServerDescription;
 class DCPVideoFrame;
+class EncodedData;
+class Writer;
 
 /** @class Encoder
  *  @brief Encoder to J2K and WAV for DCP.
@@ -62,7 +62,7 @@ class DCPVideoFrame;
 class Encoder : public VideoSink, public AudioSink
 {
 public:
-       Encoder (boost::shared_ptr<const Film> f, boost::shared_ptr<const EncodeOptions> o);
+       Encoder (boost::shared_ptr<Film> f);
        virtual ~Encoder ();
 
        /** Called to indicate that a processing run is about to begin */
@@ -82,20 +82,21 @@ public:
        virtual void process_end ();
 
        float current_frames_per_second () const;
-       bool skipping () const;
-       SourceFrame video_frame () const;
+       int video_frames_out () const;
 
-protected:
+private:
        
        void frame_done ();
-       void frame_skipped ();
        
+       void write_audio (boost::shared_ptr<const AudioBuffers> audio);
+
+       void encoder_thread (ServerDescription *);
+       void terminate_worker_threads ();
+
        /** Film that we are encoding */
-       boost::shared_ptr<const Film> _film;
-       /** Options */
-       boost::shared_ptr<const EncodeOptions> _opt;
+       boost::shared_ptr<Film> _film;
 
-       /** Mutex for _time_history, _just_skipped and _last_frame */
+       /** Mutex for _time_history and _last_frame */
        mutable boost::mutex _history_mutex;
        /** List of the times of completion of the last _history_size frames;
            first is the most recently completed.
@@ -103,41 +104,24 @@ protected:
        std::list<struct timeval> _time_history;
        /** Number of frames that we should keep history for */
        static int const _history_size;
-       /** true if the last frame we processed was skipped (because it was already done) */
-       bool _just_skipped;
 
        /** Number of video frames received so far */
-       SourceFrame _video_frame;
-       /** Number of audio frames received so far */
-       int64_t _audio_frame;
-
-private:
-       void close_sound_files ();
-       void write_audio (boost::shared_ptr<const AudioBuffers> audio);
-
-       void encoder_thread (ServerDescription *);
-       void terminate_worker_threads ();
-       void link (std::string, std::string) const;
+       SourceFrame _video_frames_in;
+       /** Number of video frames written for the DCP so far */
+       int _video_frames_out;
 
 #if HAVE_SWRESAMPLE    
        SwrContext* _swr_context;
 #endif
 
-       /** List of links that we need to create when all frames have been processed;
-        *  such that we need to call link (first, second) for each member of this list.
-        *  In other words, `first' is a `real' frame and `second' should be a link to `first'.
-        */
-       std::list<std::pair<int, int> > _links_required;
-
-       std::vector<SNDFILE*> _sound_files;
-       int64_t _audio_frames_written;
-
-       boost::optional<int> _last_real_frame;
-       bool _process_end;
-       std::list<boost::shared_ptr<DCPVideoFrame> > _queue;
+       bool _have_a_real_frame;
+       bool _terminate_encoder;
+       std::list<boost::shared_ptr<DCPVideoFrame> > _encode_queue;
        std::list<boost::thread *> _worker_threads;
        mutable boost::mutex _worker_mutex;
        boost::condition _worker_condition;
+
+       boost::shared_ptr<Writer> _writer;
 };
 
 #endif
index a783cde339f646a009d7b4c3da9eea617ace0b16..69a757e2b94bb65ad5f37128fb2c896386011f51 100644 (file)
@@ -78,8 +78,8 @@ ExamineContentJob::run ()
                _film->unset_length ();
                _film->set_crop (Crop ());
                
-               shared_ptr<DecodeOptions> o (new DecodeOptions);
-               o->decode_audio = false;
+               DecodeOptions o;
+               o.decode_audio = false;
                
                Decoders decoders = decoder_factory (_film, o, this);
                
@@ -96,8 +96,7 @@ ExamineContentJob::run ()
 
                /* Get a quick decoder to get the content's length from its header */
                
-               shared_ptr<DecodeOptions> o (new DecodeOptions);
-               Decoders d = decoder_factory (_film, o, 0);
+               Decoders d = decoder_factory (_film, DecodeOptions(), 0);
                _film->set_length (d.video->length());
        
                _film->log()->log (String::compose ("Video length obtained from header as %1 frames", _film->length().get()));
index 25c8068b6fcdb02726a0b93f8163b7ef6b968e1b..36605141886aed7ec596bb024b0425bfedc23f94 100644 (file)
@@ -31,7 +31,7 @@ using std::cout;
 using boost::shared_ptr;
 using boost::optional;
 
-ExternalAudioDecoder::ExternalAudioDecoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j)
+ExternalAudioDecoder::ExternalAudioDecoder (shared_ptr<Film> f, DecodeOptions o, Job* j)
        : Decoder (f, o, j)
        , AudioDecoder (f, o, j)
 {
index 2558955eb1985f45c396d5067a753f1109ec976d..37e53bca7f5ac1453adb54d635849a266bb384af 100644 (file)
@@ -44,7 +44,7 @@ private:
 class ExternalAudioDecoder : public AudioDecoder
 {
 public:
-       ExternalAudioDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *);
+       ExternalAudioDecoder (boost::shared_ptr<Film>, DecodeOptions, Job *);
 
        bool pass ();
 
index aff3ff666d0ce38cb689565d399cec8034cdcde4..81f40564460a035856332ae46f25cacf14d1a9dd 100644 (file)
@@ -58,8 +58,9 @@ using std::list;
 using boost::shared_ptr;
 using boost::optional;
 using boost::dynamic_pointer_cast;
+using libdcp::Size;
 
-FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j)
+FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, DecodeOptions o, Job* j)
        : Decoder (f, o, j)
        , VideoDecoder (f, o, j)
        , AudioDecoder (f, o, j)
@@ -78,7 +79,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions
        setup_audio ();
        setup_subtitle ();
 
-       if (!o->video_sync) {
+       if (!o.video_sync) {
                _first_video = 0;
        }
 }
@@ -239,7 +240,7 @@ FFmpegDecoder::pass ()
                        filter_and_emit_video (_frame);
                }
 
-               if (_audio_stream && _opt->decode_audio) {
+               if (_audio_stream && _opt.decode_audio) {
                        while (avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
                                int const data_size = av_samples_get_buffer_size (
                                        0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
@@ -267,14 +268,14 @@ FFmpegDecoder::pass ()
                                _film->log()->log (String::compose ("Used only %1 bytes of %2 in packet", r, _packet.size));
                        }
 
-                       if (_opt->video_sync) {
+                       if (_opt.video_sync) {
                                out_with_sync ();
                        } else {
                                filter_and_emit_video (_frame);
                        }
                }
 
-       } else if (ffa && _packet.stream_index == ffa->id() && _opt->decode_audio) {
+       } else if (ffa && _packet.stream_index == ffa->id() && _opt.decode_audio) {
 
                int frame_finished;
                if (avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
@@ -323,7 +324,7 @@ FFmpegDecoder::pass ()
                        }
                }
                        
-       } else if (_subtitle_stream && _packet.stream_index == _subtitle_stream->id() && _opt->decode_subtitles && _first_video) {
+       } else if (_subtitle_stream && _packet.stream_index == _subtitle_stream->id() && _opt.decode_subtitles && _first_video) {
 
                int got_subtitle;
                AVSubtitle sub;
index 3b564b826fb90172b58f5121aed68197d3599ca4..9a4e65ebc2ccd7a4db041df1dae59f6f03ec03e9 100644 (file)
@@ -86,7 +86,7 @@ private:
 class FFmpegDecoder : public VideoDecoder, public AudioDecoder
 {
 public:
-       FFmpegDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *);
+       FFmpegDecoder (boost::shared_ptr<Film>, DecodeOptions, Job *);
        ~FFmpegDecoder ();
 
        float frames_per_second () const;
index f5522b74a1add591d2829cc2e00b4c0e5176d09a..8720e79e46c86e6d053dc9abbcd3f7cda844ce79 100644 (file)
@@ -39,7 +39,6 @@
 #include "ab_transcode_job.h"
 #include "transcode_job.h"
 #include "scp_dcp_job.h"
-#include "make_dcp_job.h"
 #include "log.h"
 #include "options.h"
 #include "exceptions.h"
@@ -47,7 +46,6 @@
 #include "scaler.h"
 #include "decoder_factory.h"
 #include "config.h"
-#include "check_hashes_job.h"
 #include "version.h"
 #include "ui_signaller.h"
 #include "video_decoder.h"
@@ -72,8 +70,9 @@ using boost::to_upper_copy;
 using boost::ends_with;
 using boost::starts_with;
 using boost::optional;
+using libdcp::Size;
 
-int const Film::state_version = 1;
+int const Film::state_version = 2;
 
 /** Construct a Film object in a given directory, reading any metadata
  *  file that exists in that directory.  An exception will be thrown if
@@ -89,8 +88,8 @@ Film::Film (string d, bool must_exist)
        , _dcp_content_type (0)
        , _format (0)
        , _scaler (Scaler::from_id ("bicubic"))
-       , _dcp_trim_start (0)
-       , _dcp_trim_end (0)
+       , _trim_start (0)
+       , _trim_end (0)
        , _dcp_ab (false)
        , _use_content_audio (true)
        , _audio_gain (0)
@@ -156,8 +155,8 @@ Film::Film (Film const & o)
        , _crop              (o._crop)
        , _filters           (o._filters)
        , _scaler            (o._scaler)
-       , _dcp_trim_start    (o._dcp_trim_start)
-       , _dcp_trim_end      (o._dcp_trim_end)
+       , _trim_start        (o._trim_start)
+       , _trim_end          (o._trim_end)
        , _dcp_ab            (o._dcp_ab)
        , _content_audio_stream (o._content_audio_stream)
        , _external_audio    (o._external_audio)
@@ -174,6 +173,7 @@ Film::Film (Film const & o)
        , _dci_metadata      (o._dci_metadata)
        , _size              (o._size)
        , _length            (o._length)
+       , _dcp_intrinsic_duration (o._dcp_intrinsic_duration)
        , _content_digest    (o._content_digest)
        , _content_audio_streams (o._content_audio_streams)
        , _external_audio_stream (o._external_audio_stream)
@@ -188,24 +188,14 @@ Film::~Film ()
 {
        delete _log;
 }
-         
-/** @return The path to the directory to write JPEG2000 files to */
+
 string
-Film::j2k_dir () const
+Film::video_state_identifier () const
 {
-       assert (format());
-
-       boost::filesystem::path p;
-
-       /* Start with j2c */
-       p /= "j2c";
+       assert (format ());
 
        pair<string, string> f = Filter::ffmpeg_strings (filters());
 
-       /* Write stuff to specify the filter / post-processing settings that are in use,
-          so that we don't get confused about J2K files generated using different
-          settings.
-       */
        stringstream s;
        s << format()->id()
          << "_" << content_digest()
@@ -215,19 +205,37 @@ Film::j2k_dir () const
          << "_" << j2k_bandwidth()
          << "_" << boost::lexical_cast<int> (colour_lut());
 
-       p /= s.str ();
-
-       /* Similarly for the A/B case */
        if (dcp_ab()) {
-               stringstream s;
                pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
                s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
-               p /= s.str ();
        }
-       
+
+       return s.str ();
+}
+         
+/** @return The path to the directory to write video frame info files to */
+string
+Film::info_dir () const
+{
+       boost::filesystem::path p;
+       p /= "info";
+       p /= video_state_identifier ();
        return dir (p.string());
 }
 
+string
+Film::video_mxf_dir () const
+{
+       boost::filesystem::path p;
+       return dir ("video");
+}
+
+string
+Film::video_mxf_filename () const
+{
+       return video_state_identifier() + ".mxf";
+}
+
 /** Add suitable Jobs to the JobManager to create a DCP for this Film.
  *  @param true to transcode, false to use the WAV and J2K files that are already there.
  */
@@ -249,7 +257,9 @@ Film::make_dcp (bool transcode)
        }
        
        log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? "still" : "video")));
-       log()->log (String::compose ("Content length %1", length().get()));
+       if (length()) {
+               log()->log (String::compose ("Content length %1", length().get()));
+       }
        log()->log (String::compose ("Content digest %1", content_digest()));
        log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads()));
        log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth()));
@@ -282,47 +292,18 @@ Film::make_dcp (bool transcode)
                throw MissingSettingError ("name");
        }
 
-       shared_ptr<EncodeOptions> oe (new EncodeOptions (j2k_dir(), ".j2c", dir ("wavs")));
-       oe->out_size = format()->dcp_size ();
-       oe->padding = format()->dcp_padding (shared_from_this ());
-       if (dcp_length ()) {
-               oe->video_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get());
-               if (audio_stream()) {
-                       oe->audio_range = make_pair (
-
-                               video_frames_to_audio_frames (
-                                       oe->video_range.get().first,
-                                       dcp_audio_sample_rate (audio_stream()->sample_rate()),
-                                       dcp_frame_rate (frames_per_second()).frames_per_second
-                                       ),
-                               
-                               video_frames_to_audio_frames (
-                                       oe->video_range.get().second,
-                                       dcp_audio_sample_rate (audio_stream()->sample_rate()),
-                                       dcp_frame_rate (frames_per_second()).frames_per_second
-                                       )
-                               );
-               }
-                       
-       }
-       
-       oe->video_skip = dcp_frame_rate (frames_per_second()).skip;
-
-       shared_ptr<DecodeOptions> od (new DecodeOptions);
-       od->decode_subtitles = with_subtitles ();
+       DecodeOptions od;
+       od.decode_subtitles = with_subtitles ();
 
        shared_ptr<Job> r;
 
        if (transcode) {
                if (dcp_ab()) {
-                       r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
+                       r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od, shared_ptr<Job> ())));
                } else {
-                       r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
+                       r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od, shared_ptr<Job> ())));
                }
        }
-
-       r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), od, oe, r)));
-       JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), oe, r)));
 }
 
 /** Start a job to examine our content file */
@@ -363,7 +344,7 @@ Film::encoded_frames () const
        }
 
        int N = 0;
-       for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (j2k_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
+       for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (info_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
                ++N;
                boost::this_thread::interruption_point ();
        }
@@ -406,8 +387,8 @@ Film::write_metadata () const
                f << "filter " << (*i)->id () << "\n";
        }
        f << "scaler " << _scaler->id () << "\n";
-       f << "dcp_trim_start " << _dcp_trim_start << "\n";
-       f << "dcp_trim_end " << _dcp_trim_end << "\n";
+       f << "trim_start " << _trim_start << "\n";
+       f << "trim_end " << _trim_end << "\n";
        f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
        if (_content_audio_stream) {
                f << "selected_content_audio_stream " << _content_audio_stream->to_string() << "\n";
@@ -431,6 +412,7 @@ Film::write_metadata () const
        f << "width " << _size.width << "\n";
        f << "height " << _size.height << "\n";
        f << "length " << _length.get_value_or(0) << "\n";
+       f << "dcp_intrinsic_duration " << _dcp_intrinsic_duration.get_value_or(0) << "\n";
        f << "content_digest " << _content_digest << "\n";
 
        for (vector<shared_ptr<AudioStream> >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
@@ -511,10 +493,10 @@ Film::read_metadata ()
                        _filters.push_back (Filter::from_id (v));
                } else if (k == "scaler") {
                        _scaler = Scaler::from_id (v);
-               } else if (k == "dcp_trim_start") {
-                       _dcp_trim_start = atoi (v.c_str ());
-               } else if (k == "dcp_trim_end") {
-                       _dcp_trim_end = atoi (v.c_str ());
+               } else if ( ((!version || version < 2) && k == "trim_start") || k == "trim_start") {
+                       _trim_start = atoi (v.c_str ());
+               } else if ( ((!version || version < 2) && k == "trim_end") || k == "trim_end") {
+                       _trim_end = atoi (v.c_str ());
                } else if (k == "dcp_ab") {
                        _dcp_ab = (v == "1");
                } else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) {
@@ -563,6 +545,11 @@ Film::read_metadata ()
                        if (vv) {
                                _length = vv;
                        }
+               } else if (k == "dcp_intrinsic_duration") {
+                       int const vv = atoi (v.c_str ());
+                       if (vv) {
+                               _dcp_intrinsic_duration = vv;
+                       }
                } else if (k == "content_digest") {
                        _content_digest = v;
                } else if (k == "content_audio_stream" || (!version && k == "audio_stream")) {
@@ -674,30 +661,25 @@ Film::target_audio_sample_rate () const
        /* Resample to a DCI-approved sample rate */
        double t = dcp_audio_sample_rate (audio_stream()->sample_rate());
 
-       DCPFrameRate dfr = dcp_frame_rate (frames_per_second ());
+       DCPFrameRate dfr (frames_per_second ());
 
-       /* Compensate for the fact that video will be rounded to the
-          nearest integer number of frames per second.
+       /* 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
+          look different in the DCP compared to the source (slower or faster).
+          skip/repeat doesn't come into effect here.
        */
-       if (dfr.run_fast) {
-               t *= _frames_per_second * dfr.skip / dfr.frames_per_second;
+
+       if (dfr.change_speed) {
+               t *= _frames_per_second * dfr.factor() / dfr.frames_per_second;
        }
 
        return rint (t);
 }
 
-boost::optional<int>
-Film::dcp_length () const
+int
+Film::still_duration_in_frames () const
 {
-       if (content_type() == STILL) {
-               return _still_duration * frames_per_second();
-       }
-       
-       if (!length()) {
-               return boost::optional<int> ();
-       }
-
-       return length().get() - dcp_trim_start() - dcp_trim_end();
+       return still_duration() * frames_per_second();
 }
 
 /** @return a DCI-compliant name for a DCP of this film */
@@ -828,16 +810,8 @@ Film::set_content (string c)
 {
        string check = directory ();
 
-#if BOOST_FILESYSTEM_VERSION == 3
        boost::filesystem::path slash ("/");
        string platform_slash = slash.make_preferred().string ();
-#else
-#ifdef DVDOMATIC_WINDOWS
-       string platform_slash = "\\";
-#else
-       string platform_slash = "/";
-#endif
-#endif 
 
        if (!ends_with (check, platform_slash)) {
                check += platform_slash;
@@ -871,8 +845,7 @@ Film::set_content (string c)
        */
 
        try {
-               shared_ptr<DecodeOptions> o (new DecodeOptions);
-               Decoders d = decoder_factory (shared_from_this(), o, 0);
+               Decoders d = decoder_factory (shared_from_this(), DecodeOptions(), 0);
                
                set_size (d.video->native_size ());
                set_frames_per_second (d.video->frames_per_second ());
@@ -1047,23 +1020,23 @@ Film::set_scaler (Scaler const * s)
 }
 
 void
-Film::set_dcp_trim_start (int t)
+Film::set_trim_start (int t)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _dcp_trim_start = t;
+               _trim_start = t;
        }
-       signal_changed (DCP_TRIM_START);
+       signal_changed (TRIM_START);
 }
 
 void
-Film::set_dcp_trim_end (int t)
+Film::set_trim_end (int t)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _dcp_trim_end = t;
+               _trim_end = t;
        }
-       signal_changed (DCP_TRIM_END);
+       signal_changed (TRIM_END);
 }
 
 void
@@ -1094,8 +1067,7 @@ Film::set_external_audio (vector<string> a)
                _external_audio = a;
        }
 
-       shared_ptr<DecodeOptions> o (new DecodeOptions);
-       shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), o, 0));
+       shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), DecodeOptions(), 0));
        if (decoder->audio_stream()) {
                _external_audio_stream = decoder->audio_stream ();
        }
@@ -1242,7 +1214,17 @@ Film::unset_length ()
                _length = boost::none;
        }
        signal_changed (LENGTH);
-}      
+}
+
+void
+Film::set_dcp_intrinsic_duration (int d)
+{
+       {
+               boost::mutex::scoped_lock lm (_state_mutex);
+               _dcp_intrinsic_duration = d;
+       }
+       signal_changed (DCP_INTRINSIC_DURATION);
+}
 
 void
 Film::set_content_digest (string d)
@@ -1323,3 +1305,38 @@ Film::audio_stream () const
 
        return _external_audio_stream;
 }
+
+string
+Film::info_path (int f) const
+{
+       boost::filesystem::path p;
+       p /= info_dir ();
+
+       stringstream s;
+       s.width (8);
+       s << setfill('0') << f << ".md5";
+
+       p /= s.str();
+       return p.string ();
+}
+
+string
+Film::j2c_path (int f, bool t) const
+{
+       boost::filesystem::path p;
+       p /= "j2c";
+       p /= video_state_identifier ();
+
+       stringstream s;
+       s.width (8);
+       s << setfill('0') << f << ".j2c";
+
+       if (t) {
+               s << ".tmp";
+       }
+
+       p /= s.str();
+       return p.string ();
+}
+
+
index af7ec670134bd9bdda2f32298ce9f5aa3a5c6cc0..5b65a099dc82d760f5f6e342bc6bb19719455bb1 100644 (file)
@@ -60,7 +60,11 @@ public:
        Film (Film const &);
        ~Film ();
 
-       std::string j2k_dir () const;
+       std::string info_dir () const;
+       std::string j2c_path (int f, bool t) const;
+       std::string info_path (int f) const;
+       std::string video_mxf_dir () const;
+       std::string video_mxf_filename () const;
 
        void examine_content ();
        void send_dcp_to_tms ();
@@ -88,10 +92,13 @@ public:
        void read_metadata ();
 
        libdcp::Size cropped_size (libdcp::Size) const;
-       boost::optional<int> dcp_length () const;
        std::string dci_name () const;
        std::string dcp_name () const;
 
+       boost::optional<int> dcp_intrinsic_duration () const {
+               return _dcp_intrinsic_duration;
+       }
+
        /** @return true if our state has changed since we last saved it */
        bool dirty () const {
                return _dirty;
@@ -115,8 +122,8 @@ public:
                CROP,
                FILTERS,
                SCALER,
-               DCP_TRIM_START,
-               DCP_TRIM_END,
+               TRIM_START,
+               TRIM_END,
                DCP_AB,
                CONTENT_AUDIO_STREAM,
                EXTERNAL_AUDIO,
@@ -133,6 +140,7 @@ public:
                DCI_METADATA,
                SIZE,
                LENGTH,
+               DCP_INTRINSIC_DURATION,
                CONTENT_AUDIO_STREAMS,
                SUBTITLE_STREAMS,
                FRAMES_PER_SECOND,
@@ -191,14 +199,14 @@ public:
                return _scaler;
        }
 
-       SourceFrame dcp_trim_start () const {
+       int trim_start () const {
                boost::mutex::scoped_lock lm (_state_mutex);
-               return _dcp_trim_start;
+               return _trim_start;
        }
 
-       SourceFrame dcp_trim_end () const {
+       int trim_end () const {
                boost::mutex::scoped_lock lm (_state_mutex);
-               return _dcp_trim_end;
+               return _trim_end;
        }
 
        bool dcp_ab () const {
@@ -236,6 +244,8 @@ public:
                return _still_duration;
        }
 
+       int still_duration_in_frames () const;
+
        boost::shared_ptr<SubtitleStream> subtitle_stream () const {
                boost::mutex::scoped_lock lm (_state_mutex);
                return _subtitle_stream;
@@ -324,8 +334,8 @@ public:
        void set_bottom_crop (int);
        void set_filters (std::vector<Filter const *>);
        void set_scaler (Scaler const *);
-       void set_dcp_trim_start (int);
-       void set_dcp_trim_end (int);
+       void set_trim_start (int);
+       void set_trim_end (int);
        void set_dcp_ab (bool);
        void set_content_audio_stream (boost::shared_ptr<AudioStream>);
        void set_external_audio (std::vector<std::string>);
@@ -343,6 +353,7 @@ public:
        void set_size (libdcp::Size);
        void set_length (SourceFrame);
        void unset_length ();
+       void set_dcp_intrinsic_duration (int);
        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> >);
@@ -367,6 +378,7 @@ private:
 
        void signal_changed (Property);
        void examine_content_finished ();
+       std::string video_state_identifier () const;
 
        /** Complete path to directory containing the film metadata;
         *  must not be relative.
@@ -399,9 +411,9 @@ private:
        /** Scaler algorithm to use */
        Scaler const * _scaler;
        /** Frames to trim off the start of the DCP */
-       int _dcp_trim_start;
+       int _trim_start;
        /** Frames to trim off the end of the DCP */
-       int _dcp_trim_end;
+       int _trim_end;
        /** true to create an A/B comparison DCP, where the left half of the image
            is the video without any filters or post-processing, and the right half
            has the specified filters and post-processing.
@@ -443,10 +455,11 @@ private:
 
        /* Data which are cached to speed things up */
 
-       /** libdcp::Size, in pixels, of the source (ignoring cropping) */
+       /** Size, in pixels, of the source (ignoring cropping) */
        libdcp::Size _size;
        /** The length of the source, in video frames (as far as we know) */
        boost::optional<SourceFrame> _length;
+       boost::optional<int> _dcp_intrinsic_duration;
        /** MD5 digest of our content file */
        std::string _content_digest;
        /** The audio streams in our content */
index 446cc111dcdeeb7f0e22027969213aab6353a4fc..9a662f90fd2b2d990adbc931dc74d6a804187f32 100644 (file)
  */
 
 #include "filter.h"
+extern "C" {
+#include <libavfilter/avfilter.h>
+#include <libpostproc/postprocess.h>
+}
 
 using namespace std;
 
@@ -29,12 +33,14 @@ vector<Filter const *> Filter::_filters;
 
 /** @param i Our id.
  *  @param n User-visible name.
+ *  @param c User-visible category.
  *  @param v String for a FFmpeg video filter descriptor, or "".
  *  @param p String for a FFmpeg post-processing descriptor, or "".
  */
-Filter::Filter (string i, string n, string v, string p)
+Filter::Filter (string i, string n, string c, string v, string p)
        : _id (i)
        , _name (n)
+       , _category (c)
        , _vf (v)
        , _pp (p)
 {
@@ -57,30 +63,46 @@ Filter::setup_filters ()
 {
        /* Note: "none" is a magic id name, so don't use it here */
           
-       _filters.push_back (new Filter ("pphb", "Horizontal deblocking filter", "", "hb"));
-       _filters.push_back (new Filter ("ppvb", "Vertical deblocking filter", "", "vb"));
-       _filters.push_back (new Filter ("ppha", "Horizontal deblocking filter A", "", "ha"));
-       _filters.push_back (new Filter ("ppva", "Vertical deblocking filter A", "", "va"));
-       _filters.push_back (new Filter ("pph1", "Experimental horizontal deblocking filter 1", "", "h1"));
-       _filters.push_back (new Filter ("pphv", "Experimental vertical deblocking filter 1", "", "v1"));
-       _filters.push_back (new Filter ("ppdr", "Deringing filter", "", "dr"));
-       _filters.push_back (new Filter ("pplb", "Linear blend deinterlacer", "", "lb"));
-       _filters.push_back (new Filter ("ppli", "Linear interpolating deinterlacer", "", "li"));
-       _filters.push_back (new Filter ("ppci", "Cubic interpolating deinterlacer", "", "ci"));
-       _filters.push_back (new Filter ("ppmd", "Median deinterlacer", "", "md"));
-       _filters.push_back (new Filter ("ppfd", "FFMPEG deinterlacer", "", "fd"));
-       _filters.push_back (new Filter ("ppl5", "FIR low-pass deinterlacer", "", "l5"));
-       _filters.push_back (new Filter ("mcdeint", "Motion compensating deinterlacer", "mcdeint", ""));
-       _filters.push_back (new Filter ("kerndeint", "Kernel deinterlacer", "kerndeint", ""));
-       _filters.push_back (new Filter ("yadif", "Yet Another Deinterlacing Filter", "yadif", ""));
-       _filters.push_back (new Filter ("pptn", "Temporal noise reducer", "", "tn"));
-       _filters.push_back (new Filter ("ppfq", "Force quantizer", "", "fq"));
-       _filters.push_back (new Filter ("gradfun", "Gradient debander", "gradfun", ""));
-       _filters.push_back (new Filter ("unsharp", "Unsharp mask and Gaussian blur", "unsharp", ""));
-       _filters.push_back (new Filter ("denoise3d", "3D denoiser", "denoise3d", ""));
-       _filters.push_back (new Filter ("hqdn3d", "High quality 3D denoiser", "hqdn3d", ""));
-       _filters.push_back (new Filter ("telecine", "Telecine filter", "telecine", ""));
-       _filters.push_back (new Filter ("ow", "Overcomplete wavelet denoiser", "mp=ow", ""));
+       maybe_add ("pphb",      "Horizontal deblocking filter",                "De-blocking",     "",          "hb");
+       maybe_add ("ppvb",      "Vertical deblocking filter",                  "De-blocking",     "",          "vb");
+       maybe_add ("ppha",      "Horizontal deblocking filter A",              "De-blocking",     "",          "ha");
+       maybe_add ("ppva",      "Vertical deblocking filter A",                "De-blocking",     "",          "va");
+       maybe_add ("pph1",      "Experimental horizontal deblocking filter 1", "De-blocking",     "",          "h1");
+       maybe_add ("pphv",      "Experimental vertical deblocking filter 1",   "De-blocking",     "",          "v1");
+       maybe_add ("ppdr",      "Deringing filter",                            "Misc",            "",          "dr");
+       maybe_add ("pplb",      "Linear blend deinterlacer",                   "De-interlacing",  "",          "lb");
+       maybe_add ("ppli",      "Linear interpolating deinterlacer",           "De-interlacing",  "",          "li");
+       maybe_add ("ppci",      "Cubic interpolating deinterlacer",            "De-interlacing",  "",          "ci");
+       maybe_add ("ppmd",      "Median deinterlacer",                         "De-interlacing",  "",          "md");
+       maybe_add ("ppfd",      "FFMPEG deinterlacer",                         "De-interlacing",  "",          "fd");
+       maybe_add ("ppl5",      "FIR low-pass deinterlacer",                   "De-interlacing",  "",          "l5");
+       maybe_add ("mcdeint",   "Motion compensating deinterlacer",            "De-interlacing",  "mcdeint",   "");
+       maybe_add ("kerndeint", "Kernel deinterlacer",                         "De-interlacing",  "kerndeint", "");
+       maybe_add ("yadif",     "Yet Another Deinterlacing Filter",            "De-interlacing",  "yadif",     "");
+       maybe_add ("pptn",      "Temporal noise reducer",                      "Noise reduction", "",          "tn");
+       maybe_add ("ppfq",      "Force quantizer",                             "Misc",            "",          "fq");
+       maybe_add ("gradfun",   "Gradient debander",                           "Misc",            "gradfun",   "");
+       maybe_add ("unsharp",   "Unsharp mask and Gaussian blur",              "Misc",            "unsharp",   "");
+       maybe_add ("denoise3d", "3D denoiser",                                 "Noise reduction", "denoise3d", "");
+       maybe_add ("hqdn3d",    "High quality 3D denoiser",                    "Noise reduction", "hqdn3d",    "");
+       maybe_add ("telecine",  "Telecine filter",                             "Misc",            "telecine",  "");
+       maybe_add ("ow",        "Overcomplete wavelet denoiser",               "Noise reduction", "mp=ow",     "");
+}
+
+void
+Filter::maybe_add (string i, string n, string c, string v, string p)
+{
+       if (!v.empty ()) {
+               if (avfilter_get_by_name (i.c_str())) {
+                       _filters.push_back (new Filter (i, n, c, v, p));
+               }
+       } else if (!p.empty ()) {
+               pp_mode* m = pp_get_mode_by_name_and_quality (p.c_str(), PP_QUALITY_MAX);
+               if (m) {
+                       _filters.push_back (new Filter (i, n, c, v, p));
+                       pp_free_mode (m);
+               }
+       }
 }
 
 /** @param filters Set of filters.
index 20c55049c4367dad7fb8dae691e437078245fb34..205d92482b3aa16d3fd134476335e6b6c77336e6 100644 (file)
@@ -33,7 +33,7 @@
 class Filter
 {
 public:
-       Filter (std::string, std::string, std::string, std::string);
+       Filter (std::string, std::string, std::string, std::string, std::string);
 
        /** @return our id */
        std::string id () const {
@@ -54,6 +54,10 @@ public:
        std::string pp () const {
                return _pp;
        }
+
+       std::string category () const {
+               return _category;
+       }
        
        static std::vector<Filter const *> all ();
        static Filter const * from_id (std::string);
@@ -66,6 +70,7 @@ private:
        std::string _id;
        /** user-visible name */
        std::string _name;
+       std::string _category;
        /** string for a FFmpeg video filter descriptor */
        std::string _vf;
        /** string for a FFmpeg post-processing descriptor */
@@ -73,6 +78,7 @@ private:
 
        /** all available filters */
        static std::vector<Filter const *> _filters;
+       static void maybe_add (std::string, std::string, std::string, std::string, std::string);
 };
 
 #endif
index 6cd7dc2cbb52f38e8f9eb9db971a1f8d4bdc2d7e..b0991a2da9d53f2e7ac45a17da509d5db4975a40 100644 (file)
@@ -47,6 +47,7 @@ using std::stringstream;
 using std::string;
 using std::list;
 using boost::shared_ptr;
+using libdcp::Size;
 
 /** Construct a FilterGraph for the settings in a film.
  *  @param film Film.
@@ -67,8 +68,6 @@ FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp:
 
        filters += crop_string (Position (film->crop().left, film->crop().top), film->cropped_size (decoder->native_size()));
 
-       avfilter_register_all ();
-       
        AVFilterGraph* graph = avfilter_graph_alloc();
        if (graph == 0) {
                throw DecodeError ("Could not create filter graph.");
index 6615e16e04c202732380dfcd56aae343d21e3c8c..016c21fdea09fb60d45636f91d2ee9b5d888879b 100644 (file)
@@ -35,6 +35,7 @@ using std::setprecision;
 using std::stringstream;
 using std::vector;
 using boost::shared_ptr;
+using libdcp::Size;
 
 vector<Format const *> Format::_formats;
 
index feda09ec578bb41baeda31f033c7b770d650a45a..0ec6bd26c7ef83cc0c004b1c370850f7ed908c4b 100644 (file)
@@ -42,6 +42,7 @@ extern "C" {
 
 using namespace std;
 using namespace boost;
+using libdcp::Size;
 
 void
 Image::swap (Image& other)
@@ -95,11 +96,15 @@ Image::components () const
 }
 
 shared_ptr<Image>
-Image::scale (libdcp::Size out_size, Scaler const * scaler, bool aligned) const
+Image::scale (libdcp::Size out_size, Scaler const * scaler, bool result_aligned) const
 {
        assert (scaler);
+       /* Empirical testing suggests that sws_scale() will crash if
+          the input image is not aligned.
+       */
+       assert (aligned ());
 
-       shared_ptr<Image> scaled (new SimpleImage (pixel_format(), out_size, aligned));
+       shared_ptr<Image> scaled (new SimpleImage (pixel_format(), out_size, result_aligned));
 
        struct SwsContext* scale_context = sws_getContext (
                size().width, size().height, pixel_format(),
@@ -124,14 +129,18 @@ Image::scale (libdcp::Size out_size, Scaler const * scaler, bool aligned) const
  *  @param scaler Scaler to use.
  */
 shared_ptr<Image>
-Image::scale_and_convert_to_rgb (libdcp::Size out_size, int padding, Scaler const * scaler, bool aligned) const
+Image::scale_and_convert_to_rgb (libdcp::Size out_size, int padding, Scaler const * scaler, bool result_aligned) const
 {
        assert (scaler);
+       /* Empirical testing suggests that sws_scale() will crash if
+          the input image is not aligned.
+       */
+       assert (aligned ());
 
        libdcp::Size content_size = out_size;
        content_size.width -= (padding * 2);
 
-       shared_ptr<Image> rgb (new SimpleImage (PIX_FMT_RGB24, content_size, aligned));
+       shared_ptr<Image> rgb (new SimpleImage (PIX_FMT_RGB24, content_size, result_aligned));
 
        struct SwsContext* scale_context = sws_getContext (
                size().width, size().height, pixel_format(),
@@ -152,7 +161,7 @@ Image::scale_and_convert_to_rgb (libdcp::Size out_size, int padding, Scaler cons
           scheme of things.
        */
        if (padding > 0) {
-               shared_ptr<Image> padded_rgb (new SimpleImage (PIX_FMT_RGB24, out_size, aligned));
+               shared_ptr<Image> padded_rgb (new SimpleImage (PIX_FMT_RGB24, out_size, result_aligned));
                padded_rgb->make_black ();
 
                /* XXX: we are cheating a bit here; we know the frame is RGB so we can
@@ -231,8 +240,8 @@ Image::crop (Crop crop, bool aligned) const
                
                for (int y = 0; y < cropped_size.height; ++y) {
                        memcpy (out_p, in_p + crop_left_in_bytes, cropped_width_in_bytes);
-                       in_p += line_size()[c];
-                       out_p += out->line_size()[c];
+                       in_p += stride()[c];
+                       out_p += out->stride()[c];
                }
        }
 
@@ -244,13 +253,25 @@ Image::make_black ()
 {
        switch (_pixel_format) {
        case PIX_FMT_YUV420P:
-       case PIX_FMT_YUV422P10LE:
        case PIX_FMT_YUV422P:
                memset (data()[0], 0, lines(0) * stride()[0]);
-               memset (data()[1], 0x80, lines(1) * stride()[1]);
-               memset (data()[2], 0x80, lines(2) * stride()[2]);
+               memset (data()[1], 0x7f, lines(1) * stride()[1]);
+               memset (data()[2], 0x7f, lines(2) * stride()[2]);
                break;
 
+       case PIX_FMT_YUV422P10LE:
+               memset (data()[0], 0, lines(0) * stride()[0]);
+               for (int i = 1; i < 3; ++i) {
+                       int16_t* p = reinterpret_cast<int16_t*> (data()[i]);
+                       for (int y = 0; y < size().height; ++y) {
+                               for (int x = 0; x < line_size()[i] / 2; ++x) {
+                                       p[x] = (1 << 9) - 1;
+                               }
+                               p += stride()[i] / 2;
+                       }
+               }
+               break;
+               
        case PIX_FMT_RGB24:             
                memset (data()[0], 0, lines(0) * stride()[0]);
                break;
@@ -349,7 +370,7 @@ Image::bytes_per_pixel (int c) const
                        return 0.5;
                }
        case PIX_FMT_YUV422P10LE:
-               if (c == 1) {
+               if (c == 0) {
                        return 2;
                } else {
                        return 1;
@@ -472,6 +493,12 @@ SimpleImage::size () const
        return _size;
 }
 
+bool
+SimpleImage::aligned () const
+{
+       return _aligned;
+}
+
 FilterBufferImage::FilterBufferImage (AVPixelFormat p, AVFilterBufferRef* b)
        : Image (p)
        , _buffer (b)
@@ -509,6 +536,13 @@ FilterBufferImage::size () const
        return libdcp::Size (_buffer->video->w, _buffer->video->h);
 }
 
+bool
+FilterBufferImage::aligned () const
+{
+       /* XXX? */
+       return true;
+}
+
 RGBPlusAlphaImage::RGBPlusAlphaImage (shared_ptr<const Image> im)
        : SimpleImage (im->pixel_format(), im->size(), false)
 {
index adee8bc4d1e5196d9c3e086322b1e68c9c3b00c6..23f13a648c8b8cd29d16d1c25772d3dd50b89bf1 100644 (file)
@@ -65,9 +65,11 @@ public:
        /** @return Array of strides for each line (including any alignment padding bytes) */
        virtual int * stride () const = 0;
 
-       /** @return libdcp::Size of the image, in pixels */
+       /** @return Size of the image, in pixels */
        virtual libdcp::Size size () const = 0;
 
+       virtual bool aligned () const = 0;
+
        int components () const;
        int lines (int) const;
 
@@ -107,6 +109,7 @@ public:
        int * line_size () const;
        int * stride () const;
        libdcp::Size size () const;
+       bool aligned () const;
 
 private:
        /* Not allowed */
@@ -131,6 +134,7 @@ public:
        int * line_size () const;
        int * stride () const;
        libdcp::Size size () const;
+       bool aligned () const;
 
 protected:
        void allocate ();
index 5ebd6c8e1ccaba8ba498b6d453ad127573d6c0c3..99b9e1d340c47e5bfe8784b568ec486da754bb87 100644 (file)
 
 using std::cout;
 using boost::shared_ptr;
+using libdcp::Size;
 
 ImageMagickDecoder::ImageMagickDecoder (
-       boost::shared_ptr<Film> f, boost::shared_ptr<const DecodeOptions> o, Job* j)
+       boost::shared_ptr<Film> f, DecodeOptions o, Job* j)
        : Decoder (f, o, j)
        , VideoDecoder (f, o, j)
 {
@@ -70,7 +71,7 @@ bool
 ImageMagickDecoder::pass ()
 {
        if (_iter == _files.end()) {
-               if (!_film->dcp_length() || video_frame() >= _film->dcp_length().get()) {
+               if (video_frame() >= _film->still_duration_in_frames()) {
                        return true;
                }
 
@@ -97,7 +98,7 @@ ImageMagickDecoder::pass ()
 
        delete magick_image;
 
-       image = image->crop (_film->crop(), false);
+       image = image->crop (_film->crop(), true);
        
        emit_video (image, 0);
 
index c4795b003732dd3c500d5e326df6de7fbecca131..84a6f15f9aa8b63d762ada2deb5ac94ccd756646 100644 (file)
@@ -26,7 +26,7 @@ namespace Magick {
 class ImageMagickDecoder : public VideoDecoder
 {
 public:
-       ImageMagickDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *);
+       ImageMagickDecoder (boost::shared_ptr<Film>, DecodeOptions, Job *);
 
        float frames_per_second () const {
                /* We don't know */
index 06cff04956b8b1a28fde9ff31c493def5a5930f7..7459700eacd43b783da9b61673866f8535c929b4 100644 (file)
@@ -28,7 +28,7 @@
 using namespace std;
 
 Log::Log ()
-       : _level (VERBOSE)
+       : _level (STANDARD)
 {
 
 }
index 2b7a080fc91aea71f522570205cd5053246d7cce..182fb306c695d1008b4b8be8d50732af4cddda55 100644 (file)
@@ -81,7 +81,7 @@ Matcher::process_end ()
                
                _log->log (String::compose ("Emitting %1 frames of black video", black_video_frames));
 
-               shared_ptr<Image> black (new SimpleImage (_pixel_format.get(), _size.get(), false));
+               shared_ptr<Image> black (new SimpleImage (_pixel_format.get(), _size.get(), true));
                black->make_black ();
                for (int i = 0; i < black_video_frames; ++i) {
                        Video (black, i != 0, shared_ptr<Subtitle>());
index 2f2f44b641e26ad37644beffaa4d2789aa5f653c..2cd7dffdedaff57a9ba8bc107045dc710d0bbb03 100644 (file)
 
 */
 
-/** @file src/options.h
- *  @brief Options for a transcoding operation.
- */
-
-#include <string>
-#include <iomanip>
-#include <sstream>
-#include <boost/optional.hpp>
-#include "util.h"
+#ifndef DVDOMATIC_OPTIONS_H
+#define DVDOMATIC_OPTIONS_H
 
-/** @class EncodeOptions
- *  @brief EncodeOptions for an encoding operation.
- *
- *  These are settings which may be different, in different circumstances, for
- *  the same film; ie they are options for a particular operation.
+/** @file src/options.h
+ *  @brief Options for a decoding operation.
  */
-class EncodeOptions
-{
-public:
-
-       EncodeOptions (std::string f, std::string e, std::string m)
-               : padding (0)
-               , video_skip (0)
-               , _frame_out_path (f)
-               , _frame_out_extension (e)
-               , _multichannel_audio_out_path (m)
-       {}
-
-       /** @return The path to write video frames to */
-       std::string frame_out_path () const {
-               return _frame_out_path;
-       }
-
-       /** @param f Source frame index.
-        *  @param t true to return a temporary file path, otherwise a permanent one.
-        *  @return The path to write this video frame to.
-        */
-       std::string frame_out_path (SourceFrame f, bool t) const {
-               std::stringstream s;
-               s << _frame_out_path << "/";
-               s.width (8);
-               s << std::setfill('0') << f << _frame_out_extension;
-
-               if (t) {
-                       s << ".tmp";
-               }
-
-               return s.str ();
-       }
-
-       std::string hash_out_path (SourceFrame f, bool t) const {
-               return frame_out_path (f, t) + ".md5";
-       }
-
-       /** @return Path to write multichannel audio data to */
-       std::string multichannel_audio_out_path () const {
-               return _multichannel_audio_out_path;
-       }
-
-       /** @param c Audio channel index.
-        *  @param t true to return a temporary file path, otherwise a permanent one.
-        *  @return The path to write this audio file to.
-        */
-       std::string multichannel_audio_out_path (int c, bool t) const {
-               std::stringstream s;
-               s << _multichannel_audio_out_path << "/" << (c + 1) << ".wav";
-               if (t) {
-                       s << ".tmp";
-               }
-
-               return s.str ();
-       }
-
-       libdcp::Size out_size;      ///< size of output images
-       int padding;                ///< number of pixels of padding (in terms of the output size) each side of the image
-
-       /** Range of video frames to encode (in DCP frames) */
-       boost::optional<std::pair<int, int> > video_range;
-       /** Range of audio frames to decode (in the DCP's sampling rate) */
-       boost::optional<std::pair<int64_t, int64_t> > audio_range;
-       
-       /** Skip frames such that we don't decode any frame where (index % decode_video_skip) != 0; e.g.
-        *  1 for every frame, 2 for every other frame, etc.
-        */
-       SourceFrame video_skip; 
-
-private:
-       /** Path of the directory to write video frames to */
-       std::string _frame_out_path;
-       /** Extension to use for video frame files (including the leading .) */
-       std::string _frame_out_extension;
-       /** Path of the directory to write audio files to */
-       std::string _multichannel_audio_out_path;
-};
-
 
 class DecodeOptions
 {
@@ -126,3 +37,5 @@ public:
        bool decode_subtitles;
        bool video_sync;
 };
+
+#endif
index 22129f56cdf05e2edadda7baedce3378e012160f..3d941888e76585350f461417893d9bd4daab32dd 100644 (file)
@@ -161,12 +161,7 @@ SCPDCPJob::run ()
        
        for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (dcp_dir); i != boost::filesystem::directory_iterator(); ++i) {
                
-               /* Aah, the sweet smell of progress */
-#if BOOST_FILESYSTEM_VERSION == 3              
                string const leaf = boost::filesystem::path(*i).leaf().generic_string ();
-#else
-               string const leaf = i->leaf ();
-#endif
                
                set_status ("copying " + leaf);
                
index 1bb8f205ed65e734606d82196cacd3cf91ae115e..d75ab0fb679d05f894638158c8ec16db79d63153 100644 (file)
@@ -45,6 +45,7 @@ using boost::algorithm::is_any_of;
 using boost::algorithm::split;
 using boost::thread;
 using boost::bind;
+using libdcp::Size;
 
 /** Create a server description from a string of metadata returned from as_metadata().
  *  @param v Metadata.
index b4ac14285714b1e085a9fffcee5d80daf1906fc9..bd5f0c87969cc76b473c4af506c0c88e913ec235 100644 (file)
@@ -27,6 +27,7 @@
 
 using namespace std;
 using namespace boost;
+using libdcp::Size;
 
 /** Construct a TimedSubtitle.  This is a subtitle image, position,
  *  and a range of time over which it should be shown.
index dfb9b107192748f7dbe8c6b4f2280cd69fa05057..e9a59c743ad3fc81e12abceb3c320f11a4db485d 100644 (file)
@@ -37,13 +37,12 @@ using std::setprecision;
 using boost::shared_ptr;
 
 /** @param s Film to use.
- *  @param o Options.
+ *  @param o Decode options.
  *  @param req Job that must be completed before this job is run.
  */
-TranscodeJob::TranscodeJob (shared_ptr<Film> f, shared_ptr<const DecodeOptions> od, shared_ptr<const EncodeOptions> oe, shared_ptr<Job> req)
+TranscodeJob::TranscodeJob (shared_ptr<Film> f, DecodeOptions o, shared_ptr<Job> req)
        : Job (f, req)
-       , _decode_opt (od)
-       , _encode_opt (oe)
+       , _decode_opt (o)
 {
        
 }
@@ -62,13 +61,16 @@ TranscodeJob::run ()
                _film->log()->log ("Transcode job starting");
                _film->log()->log (String::compose ("Audio delay is %1ms", _film->audio_delay()));
 
-               _encoder.reset (new Encoder (_film, _encode_opt));
+               _encoder.reset (new Encoder (_film));
                Transcoder w (_film, _decode_opt, this, _encoder);
                w.go ();
                set_progress (1);
                set_state (FINISHED_OK);
 
+               _film->set_dcp_intrinsic_duration (_encoder->video_frames_out ());
+
                _film->log()->log ("Transcode job completed successfully");
+               _film->log()->log (String::compose ("DCP intrinsic duration is %1", _encoder->video_frames_out()));
 
        } catch (std::exception& e) {
 
@@ -87,11 +89,6 @@ TranscodeJob::status () const
                return "0%";
        }
 
-       if (_encoder->skipping () && !finished ()) {
-               return "skipping already-encoded frames";
-       }
-               
-       
        float const fps = _encoder->current_frames_per_second ();
        if (fps == 0) {
                return Job::status ();
@@ -116,11 +113,19 @@ TranscodeJob::remaining_time () const
                return 0;
        }
 
-       if (!_film->dcp_length()) {
+       if (!_film->length()) {
                return 0;
        }
 
+       /* 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) {
+               length /= 2;
+       }
+       /* If we are repeating it shouldn't affect transcode time, so don't take it into account */
+
        /* We assume that dcp_length() is valid, if it is set */
-       SourceFrame const left = _film->dcp_trim_start() + _film->dcp_length().get() - _encoder->video_frame();
+       int const left = length - _encoder->video_frames_out();
        return left / fps;
 }
index 97f655e15c212e78aba5ad9f6564dbfc84f9f7e9..8f78e7f6a899b8d3e5dda3f11da4adce2a1fdfcc 100644 (file)
 
 #include <boost/shared_ptr.hpp>
 #include "job.h"
+#include "options.h"
 
 class Encoder;
-class DecodeOptions;
-class EncodeOptions;
 
 /** @class TranscodeJob
  *  @brief A job which transcodes from one format to another.
@@ -34,7 +33,7 @@ class EncodeOptions;
 class TranscodeJob : public Job
 {
 public:
-       TranscodeJob (boost::shared_ptr<Film> f, boost::shared_ptr<const DecodeOptions> od, boost::shared_ptr<const EncodeOptions> oe, boost::shared_ptr<Job> req);
+       TranscodeJob (boost::shared_ptr<Film> f, DecodeOptions od, boost::shared_ptr<Job> req);
        
        std::string name () const;
        void run ();
@@ -44,7 +43,6 @@ protected:
        int remaining_time () const;
 
 private:
-       boost::shared_ptr<const DecodeOptions> _decode_opt;
-       boost::shared_ptr<const EncodeOptions> _encode_opt;
+       DecodeOptions _decode_opt;
        boost::shared_ptr<Encoder> _encoder;
 };
index 87a1fb3f28c8435587e7601b11de13cde3b08a30..93963761e2c3d8a7fdd74bfa4c83119b50102367 100644 (file)
@@ -48,7 +48,7 @@ using boost::dynamic_pointer_cast;
  *  @param j Job that we are running under, or 0.
  *  @param e Encoder to use.
  */
-Transcoder::Transcoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j, shared_ptr<Encoder> e)
+Transcoder::Transcoder (shared_ptr<Film> f, DecodeOptions o, Job* j, shared_ptr<Encoder> e)
        : _job (j)
        , _encoder (e)
        , _decoders (decoder_factory (f, o, j))
index b50113742369c817aa3e0b57d28158f31d111b41..786010869d35bb04f4528b3827601a62983a3a42 100644 (file)
@@ -36,8 +36,6 @@ class Gain;
 class VideoDecoder;
 class AudioDecoder;
 class DelayLine;
-class EncodeOptions;
-class DecodeOptions;
 
 /** @class Transcoder
  *  @brief A class which takes a FilmState and some Options, then uses those to transcode a Film.
@@ -50,7 +48,7 @@ class Transcoder
 public:
        Transcoder (
                boost::shared_ptr<Film> f,
-               boost::shared_ptr<const DecodeOptions> o,
+               DecodeOptions o,
                Job* j,
                boost::shared_ptr<Encoder> e
                );
index 7f370b89647557199657f47b4d47037ec5b552c0..340b76b57bfaad0eb762ea98ba73a64497d351b8 100644 (file)
@@ -26,6 +26,7 @@
 #include <iomanip>
 #include <iostream>
 #include <fstream>
+#include <climits>
 #ifdef DVDOMATIC_POSIX
 #include <execinfo.h>
 #include <cxxabi.h>
@@ -58,9 +59,11 @@ extern "C" {
 #include "dcp_content_type.h"
 #include "filter.h"
 #include "sound_processor.h"
+#include "config.h"
 
 using namespace std;
 using namespace boost;
+using libdcp::Size;
 
 thread::id ui_thread;
 
@@ -230,6 +233,8 @@ seconds (struct timeval t)
 void
 dvdomatic_setup ()
 {
+       avfilter_register_all ();
+       
        Format::setup_formats ();
        DCPContentType::setup_dcp_content_types ();
        Scaler::setup_scalers ();
@@ -330,25 +335,100 @@ md5_digest (string file)
        return s.str ();
 }
 
-/** @param fps Arbitrary frames-per-second value.
- *  @return DCPFrameRate for this frames-per-second.
- */
-DCPFrameRate
-dcp_frame_rate (float fps)
+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);
+}
+
+class FrameRateCandidate
+{
+public:
+       FrameRateCandidate (float source_, int dcp_)
+               : source (source_)
+               , dcp (dcp_)
+       {}
+
+       bool skip () const {
+               return !about_equal (source, dcp) && source > dcp;
+       }
+
+       bool repeat () const {
+               return !about_equal (source, dcp) && source < dcp;
+       }
+
+       float source;
+       int dcp;
+};
+
+/** @param fps Arbitrary source frames-per-second value */
+/** XXX: this could be slow-ish */
+DCPFrameRate::DCPFrameRate (float source_fps)
 {
-       DCPFrameRate dfr;
+       list<int> const allowed_dcp_frame_rates = Config::instance()->allowed_dcp_frame_rates ();
+
+       /* Work out what rates we could manage, including those achieved by using skip / repeat. */
+       list<FrameRateCandidate> candidates;
+
+       /* Start with the ones without skip / repeat so they will get matched in preference to skipped/repeated ones */
+       for (list<int>::const_iterator i = allowed_dcp_frame_rates.begin(); i != allowed_dcp_frame_rates.end(); ++i) {
+               candidates.push_back (FrameRateCandidate (*i, *i));
+       }
+
+       /* Then the skip/repeat ones */
+       for (list<int>::const_iterator i = allowed_dcp_frame_rates.begin(); i != allowed_dcp_frame_rates.end(); ++i) {
+               candidates.push_back (FrameRateCandidate (float (*i) / 2, *i));
+               candidates.push_back (FrameRateCandidate (float (*i) * 2, *i));
+       }
+
+       /* Pick the best one, bailing early if we hit an exact match */
+       float error = numeric_limits<float>::max ();
+       boost::optional<FrameRateCandidate> best;
+       list<FrameRateCandidate>::iterator i = candidates.begin();
+       while (i != candidates.end()) {
+               
+               if (about_equal (i->source, source_fps)) {
+                       best = *i;
+                       break;
+               }
 
-       dfr.run_fast = (fps != rint (fps));
-       dfr.frames_per_second = rint (fps);
-       dfr.skip = 1;
+               float const e = fabs (i->source - source_fps);
+               if (e < error) {
+                       error = e;
+                       best = *i;
+               }
 
-       /* XXX: somewhat arbitrary */
-       if (fps == 50) {
-               dfr.frames_per_second = 25;
-               dfr.skip = 2;
+               ++i;
        }
 
-       return dfr;
+       if (!best) {
+               throw EncodeError ("cannot find a suitable DCP frame rate for this source");
+       }
+
+       frames_per_second = best->dcp;
+       skip = best->skip ();
+       repeat = best->repeat ();
+       change_speed = !about_equal (source_fps * factor(), frames_per_second);
 }
 
 /** @param An arbitrary sampling rate.
@@ -542,6 +622,9 @@ Socket::read_definite_and_consume (uint8_t* data, int size, int timeout)
 /** Read as much data as is available, up to some limit.
  *  @param data Where to put the data.
  *  @param size Maximum amount of data to read.
+ *
+ *  XXX This method assumes that there is always lots of data to read();
+ *  if there isn't, it will hang waiting for data that will never arrive.
  */
 void
 Socket::read_indefinite (uint8_t* data, int size, int timeout)
@@ -854,11 +937,7 @@ video_frames_to_audio_frames (SourceFrame v, float audio_sample_rate, float fram
 bool
 still_image_file (string f)
 {
-#if BOOST_FILESYSTEM_VERSION == 3
        string ext = boost::filesystem::path(f).extension().string();
-#else
-       string ext = boost::filesystem::path(f).extension();
-#endif
 
        transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
        
index 77fb943e090fa8866ac7b6eb021b6bb5786c8cdb..c4940a5d795b3c615d6861c15a0bc7ba804ce2f1 100644 (file)
@@ -62,16 +62,37 @@ typedef int SourceFrame;
 
 struct DCPFrameRate
 {
+       DCPFrameRate (float);
+
+       /** @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;
+               } else if (repeat) {
+                       return 2;
+               }
+
+               return 1;
+       }
+       
        /** frames per second for the DCP */
        int frames_per_second;
-       /** Skip every `skip' frames.  e.g. if this is 1, we skip nothing;
-        *  if it's 2, we skip every other frame.
-        */
-       int skip;
-       /** true if this DCP will run its video faster than the source
-        *  (e.g. if the source is 29.97fps and we will run the DCP at 30fps)
+       /** 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'.
+        *  (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 run_fast;
+       bool change_speed;
 };
 
 enum ContentType {
@@ -157,7 +178,6 @@ struct Rect
 
 extern std::string crop_string (Position, libdcp::Size);
 extern int dcp_audio_sample_rate (int);
-extern DCPFrameRate dcp_frame_rate (float);
 extern int dcp_audio_channels (int);
 extern std::string colour_lut_index_to_name (int index);
 extern int stride_round_up (int, int const *, int);
index e0a7576eeaf5c51670cf1702c6b7b4597467db15..0fa16bc325b8b6ee8a309098c54a066359d99e1a 100644 (file)
@@ -28,7 +28,7 @@
 using boost::shared_ptr;
 using boost::optional;
 
-VideoDecoder::VideoDecoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions> o, Job* j)
+VideoDecoder::VideoDecoder (shared_ptr<Film> f, DecodeOptions o, Job* j)
        : Decoder (f, o, j)
        , _video_frame (0)
        , _last_source_time (0)
@@ -57,7 +57,7 @@ void
 VideoDecoder::repeat_last_video ()
 {
        if (!_last_image) {
-               _last_image.reset (new SimpleImage (pixel_format(), native_size(), false));
+               _last_image.reset (new SimpleImage (pixel_format(), native_size(), true));
                _last_image->make_black ();
        }
 
index b18082c69ef0c67b2dbecd6c137964edf5647adc..ef1ab041a052589f003816e26b239dd4e2ef2a9b 100644 (file)
@@ -27,7 +27,7 @@
 class VideoDecoder : public VideoSource, public virtual Decoder
 {
 public:
-       VideoDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const DecodeOptions>, Job *);
+       VideoDecoder (boost::shared_ptr<Film>, DecodeOptions, Job *);
 
        /** @return video frames per second, or 0 if unknown */
        virtual float frames_per_second () const = 0;
diff --git a/src/lib/writer.cc b/src/lib/writer.cc
new file mode 100644 (file)
index 0000000..5bd3214
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+    Copyright (C) 2012 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 <fstream>
+#include <libdcp/picture_asset.h>
+#include <libdcp/sound_asset.h>
+#include <libdcp/picture_frame.h>
+#include <libdcp/reel.h>
+#include "writer.h"
+#include "compose.hpp"
+#include "film.h"
+#include "format.h"
+#include "log.h"
+#include "dcp_video_frame.h"
+
+using std::make_pair;
+using std::pair;
+using std::string;
+using std::ifstream;
+using std::cout;
+using boost::shared_ptr;
+
+unsigned int const Writer::_maximum_frames_in_memory = 8;
+
+Writer::Writer (shared_ptr<Film> f)
+       : _film (f)
+       , _first_nonexistant_frame (0)
+       , _thread (0)
+       , _finish (false)
+       , _last_written_frame (-1)
+{
+       check_existing_picture_mxf ();
+       
+       /* Create our picture asset in a subdirectory, named according to those
+          film's parameters which affect the video output.  We will hard-link
+          it into the DCP later.
+       */
+       
+       _picture_asset.reset (
+               new libdcp::MonoPictureAsset (
+                       _film->video_mxf_dir (),
+                       _film->video_mxf_filename (),
+                       DCPFrameRate (_film->frames_per_second()).frames_per_second,
+                       _film->format()->dcp_size()
+                       )
+               );
+
+       _picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
+
+       if (_film->audio_channels() > 0) {
+               _sound_asset.reset (
+                       new libdcp::SoundAsset (
+                               _film->dir (_film->dcp_name()),
+                               "audio.mxf",
+                               DCPFrameRate (_film->frames_per_second()).frames_per_second,
+                               _film->audio_channels(),
+                               dcp_audio_sample_rate (_film->audio_stream()->sample_rate())
+                               )
+                       );
+
+               _sound_asset_writer = _sound_asset->start_write ();
+       }
+       
+       _thread = new boost::thread (boost::bind (&Writer::thread, this));
+}
+
+void
+Writer::write (shared_ptr<const EncodedData> encoded, int frame)
+{
+       boost::mutex::scoped_lock lock (_mutex);
+
+       QueueItem qi;
+       qi.type = QueueItem::FULL;
+       qi.encoded = encoded;
+       qi.frame = frame;
+       _queue.push_back (qi);
+
+       _condition.notify_all ();
+}
+
+void
+Writer::fake_write (int frame)
+{
+       boost::mutex::scoped_lock lock (_mutex);
+
+       ifstream ifi (_film->info_path (frame).c_str());
+       libdcp::FrameInfo info (ifi);
+       
+       QueueItem qi;
+       qi.type = QueueItem::FAKE;
+       qi.size = info.size;
+       qi.frame = frame;
+       _queue.push_back (qi);
+
+       _condition.notify_all ();
+}
+
+/** This method is not thread safe */
+void
+Writer::write (shared_ptr<const AudioBuffers> audio)
+{
+       _sound_asset_writer->write (audio->data(), audio->frames());
+}
+
+void
+Writer::thread ()
+{
+       while (1)
+       {
+               boost::mutex::scoped_lock lock (_mutex);
+
+               while (1) {
+                       if (_finish ||
+                           _queue.size() > _maximum_frames_in_memory ||
+                           (!_queue.empty() && _queue.front().frame == (_last_written_frame + 1))) {
+                                   
+                                   break;
+                           }
+
+                           TIMING ("writer sleeps with a queue of %1; %2 pending", _queue.size(), _pending.size());
+                           _condition.wait (lock);
+                           TIMING ("writer wakes with a queue of %1", _queue.size());
+
+                           _queue.sort ();
+               }
+
+               if (_finish && _queue.empty() && _pending.empty()) {
+                       return;
+               }
+
+               /* Write any frames that we can write; i.e. those that are in sequence */
+               while (!_queue.empty() && _queue.front().frame == (_last_written_frame + 1)) {
+                       QueueItem qi = _queue.front ();
+                       _queue.pop_front ();
+
+                       lock.unlock ();
+                       switch (qi.type) {
+                       case QueueItem::FULL:
+                       {
+                               _film->log()->log (String::compose ("Writer FULL-writes %1 to MXF", qi.frame));
+                               libdcp::FrameInfo const fin = _picture_asset_writer->write (qi.encoded->data(), qi.encoded->size());
+                               qi.encoded->write_info (_film, qi.frame, fin);
+                               _last_written = qi.encoded;
+                               break;
+                       }
+                       case QueueItem::FAKE:
+                               _film->log()->log (String::compose ("Writer FAKE-writes %1 to MXF", qi.frame));
+                               _picture_asset_writer->fake_write (qi.size);
+                               _last_written.reset ();
+                               break;
+                       case QueueItem::REPEAT:
+                       {
+                               _film->log()->log (String::compose ("Writer REPEAT-writes %1 to MXF", qi.frame));
+                               libdcp::FrameInfo const fin = _picture_asset_writer->write (_last_written->data(), _last_written->size());
+                               _last_written->write_info (_film, qi.frame, fin);
+                               break;
+                       }
+                       }
+                       lock.lock ();
+
+                       ++_last_written_frame;
+               }
+
+               while (_queue.size() > _maximum_frames_in_memory) {
+                       /* Too many frames in memory which can't yet be written to the stream.
+                          Put some in our pending list (and write FULL queue items' data to disk)
+                       */
+
+                       QueueItem qi = _queue.back ();
+                       _queue.pop_back ();
+
+                       if (qi.type == QueueItem::FULL) {
+                               lock.unlock ();
+                               _film->log()->log (String::compose ("Writer full (awaiting %1); pushes %2 to disk", _last_written_frame + 1, qi.frame));
+                               qi.encoded->write (_film, qi.frame);
+                               lock.lock ();
+                               qi.encoded.reset ();
+                       }
+
+                       _pending.push_back (qi);
+               }
+
+               while (_queue.size() < _maximum_frames_in_memory && !_pending.empty()) {
+                       /* We have some space in memory.  Fetch some frames back off disk. */
+
+                       _pending.sort ();
+                       QueueItem qi = _pending.front ();
+
+                       if (qi.type == QueueItem::FULL) {
+                               lock.unlock ();
+                               _film->log()->log (String::compose ("Writer pulls %1 back from disk", qi.frame));
+                               shared_ptr<const EncodedData> encoded;
+                               qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, false)));
+                               lock.lock ();
+                       }
+
+                       _queue.push_back (qi);
+                       _pending.remove (qi);
+               }
+       }
+
+}
+
+void
+Writer::finish ()
+{
+       if (!_thread) {
+               return;
+       }
+       
+       boost::mutex::scoped_lock lock (_mutex);
+       _finish = true;
+       _condition.notify_all ();
+       lock.unlock ();
+
+       _thread->join ();
+       delete _thread;
+       _thread = 0;
+
+       _picture_asset_writer->finalize ();
+
+       if (_sound_asset_writer) {
+               _sound_asset_writer->finalize ();
+       }
+
+       int const frames = _last_written_frame + 1;
+       int const duration = frames - _film->trim_start() - _film->trim_end();
+       
+       _film->set_dcp_intrinsic_duration (frames);
+       
+       _picture_asset->set_entry_point (_film->trim_start ());
+       _picture_asset->set_duration (duration);
+
+       /* Hard-link the video MXF into the DCP */
+
+       boost::filesystem::path from;
+       from /= _film->video_mxf_dir();
+       from /= _film->video_mxf_filename();
+       
+       boost::filesystem::path to;
+       to /= _film->dir (_film->dcp_name());
+       to /= "video.mxf";
+       
+       boost::filesystem::create_hard_link (from, to);
+
+       /* And update the asset */
+
+       _picture_asset->set_directory (_film->dir (_film->dcp_name ()));
+       _picture_asset->set_file_name ("video.mxf");
+
+       if (_sound_asset) {
+               _sound_asset->set_entry_point (_film->trim_start ());
+               _sound_asset->set_duration (duration);
+       }
+       
+       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)
+               );
+       
+       dcp.add_cpl (cpl);
+
+       cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
+                                                        _picture_asset,
+                                                        _sound_asset,
+                                                        shared_ptr<libdcp::SubtitleAsset> ()
+                                                        )
+                              ));
+
+       dcp.write_xml ();
+}
+
+/** Tell the writer that frame `f' should be a repeat of the frame before it */
+void
+Writer::repeat (int f)
+{
+       boost::mutex::scoped_lock lock (_mutex);
+
+       QueueItem qi;
+       qi.type = QueueItem::REPEAT;
+       qi.frame = f;
+       
+       _queue.push_back (qi);
+
+       _condition.notify_all ();
+}
+
+
+void
+Writer::check_existing_picture_mxf ()
+{
+       /* Try to open the existing MXF */
+       boost::filesystem::path p;
+       p /= _film->video_mxf_dir ();
+       p /= _film->video_mxf_filename ();
+       FILE* mxf = fopen (p.string().c_str(), "rb");
+       if (!mxf) {
+               return;
+       }
+
+       while (1) {
+
+               /* Read the frame info as written */
+               ifstream ifi (_film->info_path (_first_nonexistant_frame).c_str());
+               libdcp::FrameInfo info (ifi);
+
+               /* Read the data from the MXF and hash it */
+               fseek (mxf, info.offset, SEEK_SET);
+               EncodedData data (info.size);
+               fread (data.data(), 1, data.size(), mxf);
+               string const existing_hash = md5_digest (data.data(), data.size());
+               
+               if (existing_hash != info.hash) {
+                       _film->log()->log (String::compose ("Existing frame %1 failed hash check", _first_nonexistant_frame));
+                       break;
+               }
+
+               _film->log()->log (String::compose ("Have existing frame %1", _first_nonexistant_frame));
+               ++_first_nonexistant_frame;
+       }
+
+       fclose (mxf);
+}
+
+/** @return true if the fake write succeeded, otherwise false */
+bool
+Writer::can_fake_write (int frame) const
+{
+       return (frame != 0 && frame < _first_nonexistant_frame);
+}
+
+
+bool
+operator< (QueueItem const & a, QueueItem const & b)
+{
+       return a.frame < b.frame;
+}
+
+bool
+operator== (QueueItem const & a, QueueItem const & b)
+{
+       return a.frame == b.frame;
+}
diff --git a/src/lib/writer.h b/src/lib/writer.h
new file mode 100644 (file)
index 0000000..5760982
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+    Copyright (C) 2012 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 <list>
+#include <boost/shared_ptr.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/condition.hpp>
+
+class Film;
+class EncodedData;
+class AudioBuffers;
+
+namespace libdcp {
+       class MonoPictureAsset;
+       class MonoPictureAssetWriter;
+       class SoundAsset;
+       class SoundAssetWriter;
+}
+
+struct QueueItem
+{
+public:
+       enum Type {
+               FULL,
+               FAKE,
+               REPEAT
+       } type;
+
+       /** encoded data for FULL */
+       boost::shared_ptr<const EncodedData> encoded;
+       /** size of data for FAKE */
+       int size;
+       /** frame index */
+       int frame;
+};
+
+bool operator< (QueueItem const & a, QueueItem const & b);
+bool operator== (QueueItem const & a, QueueItem const & b);
+
+class Writer
+{
+public:
+       Writer (boost::shared_ptr<Film>);
+
+       bool can_fake_write (int) const;
+       
+       void write (boost::shared_ptr<const EncodedData>, int);
+       void fake_write (int);
+       void write (boost::shared_ptr<const AudioBuffers>);
+       void repeat (int f);
+       void finish ();
+
+private:
+
+       void thread ();
+       void check_existing_picture_mxf ();
+
+       boost::shared_ptr<Film> _film;
+       int _first_nonexistant_frame;
+
+       boost::thread* _thread;
+       bool _finish;
+       std::list<QueueItem> _queue;
+       mutable boost::mutex _mutex;
+       boost::condition _condition;
+       boost::shared_ptr<const EncodedData> _last_written;
+       std::list<QueueItem> _pending;
+       int _last_written_frame;
+       static const unsigned int _maximum_frames_in_memory;
+
+       boost::shared_ptr<libdcp::MonoPictureAsset> _picture_asset;
+       boost::shared_ptr<libdcp::MonoPictureAssetWriter> _picture_asset_writer;
+       boost::shared_ptr<libdcp::SoundAsset> _sound_asset;
+       boost::shared_ptr<libdcp::SoundAssetWriter> _sound_asset_writer;
+};
index cada2b0c30b683f1979af043ebf3dad96aea9a31..454565cdc447d55c386e64773c5ea48779fee939 100644 (file)
@@ -14,7 +14,6 @@ def build(bld):
                 ab_transcoder.cc
                  audio_decoder.cc
                  audio_source.cc
-                 check_hashes_job.cc
                 config.cc
                  combiner.cc
                  cross.cc
@@ -41,7 +40,6 @@ def build(bld):
                 job_manager.cc
                 log.cc
                 lut.cc
-                make_dcp_job.cc
                  matcher.cc
                  scp_dcp_job.cc
                 scaler.cc
@@ -57,6 +55,7 @@ def build(bld):
                 version.cc
                  video_decoder.cc
                  video_source.cc
+                 writer.cc
                 """
 
     obj.target = 'dvdomatic'
index d5dca2ab4eb3d185cd59e2851a2b498dcbed8883..0565d3a1c871aadf4177bb919f14e7bf4a0631a1 100644 (file)
@@ -64,7 +64,7 @@ public:
        {
                stringstream s;
                s << "Save changes to film \"" << film->name() << "\" before closing?";
-               _dialog = new wxMessageDialog (0, std_to_wx (s.str()), wxT ("Film changed"), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
+               _dialog = new wxMessageDialog (0, std_to_wx (s.str()), _("Film changed"), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
        }
 
        ~FilmChangedDialog ()
@@ -263,18 +263,14 @@ public:
                if (r == wxID_OK) {
 
                        if (boost::filesystem::exists (d->get_path())) {
-                               error_dialog (this, String::compose ("The directory %1 already exists.", d->get_path()));
+                               error_dialog (this, wxString::Format (_("The directory %s already exists"), d->get_path().c_str()));
                                return;
                        }
                        
                        maybe_save_then_delete_film ();
                        film.reset (new Film (d->get_path (), false));
                        film->log()->set_level (log_level);
-#if BOOST_FILESYSTEM_VERSION == 3              
                        film->set_name (boost::filesystem::path (d->get_path()).filename().generic_string());
-#else          
-                       film->set_name (boost::filesystem::path (d->get_path()).filename());
-#endif
                        set_film ();
                }
                
@@ -283,7 +279,7 @@ public:
 
        void file_open (wxCommandEvent &)
        {
-               wxDirDialog* c = new wxDirDialog (this, wxT ("Select film to open"), wxStandardPaths::Get().GetDocumentsDir(), wxDEFAULT_DIALOG_STYLE | wxDD_DIR_MUST_EXIST);
+               wxDirDialog* c = new wxDirDialog (this, _("Select film to open"), wxStandardPaths::Get().GetDocumentsDir(), wxDEFAULT_DIALOG_STYLE | wxDD_DIR_MUST_EXIST);
                int const r = c->ShowModal ();
                
                if (r == wxID_OK) {
@@ -293,7 +289,9 @@ public:
                                film->log()->set_level (log_level);
                                set_film ();
                        } catch (std::exception& e) {
-                               error_dialog (this, String::compose ("Could not open film at %1 (%2)", wx_to_std (c->GetPath()), e.what()));
+                               wxString p = c->GetPath ();
+                               wxCharBuffer b = p.ToUTF8 ();
+                               error_dialog (this, wxString::Format (_("Could not open film at %s (%s)"), p.data(), e.what()));
                        }
                }
 
index 900c31bfc9fca7630bc8002ba833128be88563ff..892bed3b81d0368582bec7ecf6aedc22c14d39bf 100644 (file)
@@ -26,7 +26,6 @@
 #include "film.h"
 #include "filter.h"
 #include "transcode_job.h"
-#include "make_dcp_job.h"
 #include "job_manager.h"
 #include "ab_transcode_job.h"
 #include "util.h"
@@ -160,6 +159,7 @@ main (int argc, char* argv[])
 
        bool should_stop = false;
        bool first = true;
+       bool error = false;
        while (!should_stop) {
 
                dvdomatic_sleep (5);
@@ -195,6 +195,7 @@ main (int argc, char* argv[])
 
                        if ((*i)->finished_in_error ()) {
                                ++finished_in_error;
+                               error = true;
                        }
 
                        if (!progress && (*i)->finished_in_error ()) {
@@ -210,7 +211,7 @@ main (int argc, char* argv[])
                }
        }
 
-       return 0;
+       return error ? EXIT_FAILURE : EXIT_SUCCESS;
 }
 
          
index b656a5278b5cd32ede5fc8f75e508335cc58182a..07e32a4578a0ecd44a2d825a4e97c03265c770e5 100644 (file)
@@ -46,32 +46,32 @@ ConfigDialog::ConfigDialog (wxWindow* parent)
        wxFlexGridSizer* table = new wxFlexGridSizer (3, 6, 6);
        table->AddGrowableCol (1, 1);
 
-       add_label_to_sizer (table, this, "TMS IP address");
+       add_label_to_sizer (table, this, _("TMS IP address"));
        _tms_ip = new wxTextCtrl (this, wxID_ANY);
        table->Add (_tms_ip, 1, wxEXPAND);
        table->AddSpacer (0);
 
-       add_label_to_sizer (table, this, "TMS target path");
+       add_label_to_sizer (table, this, _("TMS target path"));
        _tms_path = new wxTextCtrl (this, wxID_ANY);
        table->Add (_tms_path, 1, wxEXPAND);
        table->AddSpacer (0);
 
-       add_label_to_sizer (table, this, "TMS user name");
+       add_label_to_sizer (table, this, _("TMS user name"));
        _tms_user = new wxTextCtrl (this, wxID_ANY);
        table->Add (_tms_user, 1, wxEXPAND);
        table->AddSpacer (0);
 
-       add_label_to_sizer (table, this, "TMS password");
+       add_label_to_sizer (table, this, _("TMS password"));
        _tms_password = new wxTextCtrl (this, wxID_ANY);
        table->Add (_tms_password, 1, wxEXPAND);
        table->AddSpacer (0);
 
-       add_label_to_sizer (table, this, "Threads to use for encoding on this host");
+       add_label_to_sizer (table, this, _("Threads to use for encoding on this host"));
        _num_local_encoding_threads = new wxSpinCtrl (this);
        table->Add (_num_local_encoding_threads, 1, wxEXPAND);
        table->AddSpacer (0);
 
-       add_label_to_sizer (table, this, "Default directory for new films");
+       add_label_to_sizer (table, this, _("Default directory for new films"));
 #ifdef __WXMSW__
        _default_directory = new DirPickerCtrl (this);
 #else  
@@ -80,12 +80,12 @@ ConfigDialog::ConfigDialog (wxWindow* parent)
        table->Add (_default_directory, 1, wxEXPAND);
        table->AddSpacer (0);
 
-       add_label_to_sizer (table, this, "Default DCI name details");
+       add_label_to_sizer (table, this, _("Default DCI name details"));
        _default_dci_metadata_button = new wxButton (this, wxID_ANY, _("Edit..."));
        table->Add (_default_dci_metadata_button);
        table->AddSpacer (1);
 
-       add_label_to_sizer (table, this, "Reference scaler for A/B");
+       add_label_to_sizer (table, this, _("Reference scaler for A/B"));
        _reference_scaler = new wxChoice (this, wxID_ANY);
        vector<Scaler const *> const sc = Scaler::all ();
        for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) {
@@ -96,7 +96,7 @@ ConfigDialog::ConfigDialog (wxWindow* parent)
        table->AddSpacer (0);
 
        {
-               add_label_to_sizer (table, this, "Reference filters for A/B");
+               add_label_to_sizer (table, this, _("Reference filters for A/B"));
                wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                _reference_filters = new wxStaticText (this, wxID_ANY, wxT (""));
                s->Add (_reference_filters, 1, wxEXPAND);
@@ -106,7 +106,7 @@ ConfigDialog::ConfigDialog (wxWindow* parent)
                table->AddSpacer (0);
        }
 
-       add_label_to_sizer (table, this, "Encoding Servers");
+       add_label_to_sizer (table, this, _("Encoding Servers"));
        _servers = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (220, 100), wxLC_REPORT | wxLC_SINGLE_SEL);
        wxListItem ip;
        ip.SetId (0);
index c5682e19ebd94e773d97439beb9e5f8bd03d8a5b..c08c58ed460abb15846f0c637968119024c51be5 100644 (file)
@@ -30,31 +30,31 @@ DCIMetadataDialog::DCIMetadataDialog (wxWindow* parent, DCIMetadata dm)
        wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6);
        table->AddGrowableCol (1, 1);
 
-       add_label_to_sizer (table, this, "Audio Language (e.g. EN)");
+       add_label_to_sizer (table, this, _("Audio Language (e.g. EN)"));
        _audio_language = new wxTextCtrl (this, wxID_ANY);
        table->Add (_audio_language, 1, wxEXPAND);
 
-       add_label_to_sizer (table, this, "Subtitle Language (e.g. FR)");
+       add_label_to_sizer (table, this, _("Subtitle Language (e.g. FR)"));
        _subtitle_language = new wxTextCtrl (this, wxID_ANY);
        table->Add (_subtitle_language, 1, wxEXPAND);
        
-       add_label_to_sizer (table, this, "Territory (e.g. UK)");
+       add_label_to_sizer (table, this, _("Territory (e.g. UK)"));
        _territory = new wxTextCtrl (this, wxID_ANY);
        table->Add (_territory, 1, wxEXPAND);
 
-       add_label_to_sizer (table, this, "Rating (e.g. 15)");
+       add_label_to_sizer (table, this, _("Rating (e.g. 15)"));
        _rating = new wxTextCtrl (this, wxID_ANY);
        table->Add (_rating, 1, wxEXPAND);
 
-       add_label_to_sizer (table, this, "Studio (e.g. TCF)");
+       add_label_to_sizer (table, this, _("Studio (e.g. TCF)"));
        _studio = new wxTextCtrl (this, wxID_ANY);
        table->Add (_studio, 1, wxEXPAND);
 
-       add_label_to_sizer (table, this, "Facility (e.g. DLA)");
+       add_label_to_sizer (table, this, _("Facility (e.g. DLA)"));
        _facility = new wxTextCtrl (this, wxID_ANY);
        table->Add (_facility, 1, wxEXPAND);
 
-       add_label_to_sizer (table, this, "Package Type (e.g. OV)");
+       add_label_to_sizer (table, this, _("Package Type (e.g. OV)"));
        _package_type = new wxTextCtrl (this, wxID_ANY);
        table->Add (_package_type, 1, wxEXPAND);
 
index cb811fc1087a26839832a5b73f1bdb735d1d0ba3..b6558a881eee90169ba681cc9ff8757ff2a0b01a 100644 (file)
@@ -50,11 +50,7 @@ DirPickerCtrl::SetPath (wxString p)
        if (_path == wxStandardPaths::Get().GetDocumentsDir()) {
                _folder->SetLabel (_("My Documents"));
        } else {
-#if BOOST_FILESYSTEM_VERSION == 3              
                _folder->SetLabel (std_to_wx (filesystem::path (wx_to_std (_path)).leaf().string()));
-#else
-               _folder->SetLabel (std_to_wx (filesystem::path (wx_to_std (_path)).leaf()));
-#endif         
        }
 }
 
index f058afa54d21463f955a35e510bbbfd505735a50..634e417df4ccf01c14c02f783f4b5eb2abfa4a27 100644 (file)
@@ -92,77 +92,79 @@ void
 FilmEditor::make_film_panel ()
 {
        _film_panel = new wxPanel (_notebook);
-       _film_sizer = new wxFlexGridSizer (2, 4, 4);
-       wxBoxSizer* pad = new wxBoxSizer (wxVERTICAL);
-       pad->Add (_film_sizer, 0, wxALL, 8);
-       _film_panel->SetSizer (pad);
+       _film_sizer = new wxBoxSizer (wxVERTICAL);
+       _film_panel->SetSizer (_film_sizer);
 
-       add_label_to_sizer (_film_sizer, _film_panel, "Name");
+       wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
+       _film_sizer->Add (grid, 0, wxALL, 8);
+
+       add_label_to_sizer (grid, _film_panel, _("Name"));
        _name = new wxTextCtrl (_film_panel, wxID_ANY);
-       _film_sizer->Add (_name, 1, wxEXPAND);
+       grid->Add (_name, 1, wxEXPAND);
 
-       add_label_to_sizer (_film_sizer, _film_panel, "DCP Name");
+       add_label_to_sizer (grid, _film_panel, _("DCP Name"));
        _dcp_name = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
-       _film_sizer->Add (_dcp_name, 0, wxALIGN_CENTER_VERTICAL | wxSHRINK);
+       grid->Add (_dcp_name, 0, wxALIGN_CENTER_VERTICAL | wxSHRINK);
 
-       _use_dci_name = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Use DCI name"));
-       _film_sizer->Add (_use_dci_name, 1, wxEXPAND);
-       _edit_dci_button = new wxButton (_film_panel, wxID_ANY, wxT ("Details..."));
-       _film_sizer->Add (_edit_dci_button, 0);
+       _use_dci_name = new wxCheckBox (_film_panel, wxID_ANY, _("Use DCI name"));
+       grid->Add (_use_dci_name, 1, wxEXPAND);
+       _edit_dci_button = new wxButton (_film_panel, wxID_ANY, _("Details..."));
+       grid->Add (_edit_dci_button, 0);
 
-       add_label_to_sizer (_film_sizer, _film_panel, "Content");
-       _content = new wxFilePickerCtrl (_film_panel, wxID_ANY, wxT (""), wxT ("Select Content File"), wxT("*.*"));
-       _film_sizer->Add (_content, 1, wxEXPAND);
+       add_label_to_sizer (grid, _film_panel, _("Content"));
+       _content = new wxFilePickerCtrl (_film_panel, wxID_ANY, wxT (""), _("Select Content File"), wxT("*.*"));
+       grid->Add (_content, 1, wxEXPAND);
 
-       _trust_content_header = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Trust content's header"));
+       _trust_content_header = new wxCheckBox (_film_panel, wxID_ANY, _("Trust content's header"));
        video_control (_trust_content_header);
-       _film_sizer->Add (_trust_content_header, 1);
-       _film_sizer->AddSpacer (0);
+       grid->Add (_trust_content_header, 1);
+       grid->AddSpacer (0);
 
-       add_label_to_sizer (_film_sizer, _film_panel, "Content Type");
+       add_label_to_sizer (grid, _film_panel, _("Content Type"));
        _dcp_content_type = new wxChoice (_film_panel, wxID_ANY);
-       _film_sizer->Add (_dcp_content_type);
+       grid->Add (_dcp_content_type);
 
-       video_control (add_label_to_sizer (_film_sizer, _film_panel, "Frames Per Second"));
+       video_control (add_label_to_sizer (grid, _film_panel, _("Frames Per Second")));
        _frames_per_second = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
-       _film_sizer->Add (video_control (_frames_per_second), 1, wxALIGN_CENTER_VERTICAL);
+       grid->Add (video_control (_frames_per_second), 1, wxALIGN_CENTER_VERTICAL);
        
-       video_control (add_label_to_sizer (_film_sizer, _film_panel, "Original Size"));
+       video_control (add_label_to_sizer (grid, _film_panel, _("Original Size")));
        _original_size = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
-       _film_sizer->Add (video_control (_original_size), 1, wxALIGN_CENTER_VERTICAL);
+       grid->Add (video_control (_original_size), 1, wxALIGN_CENTER_VERTICAL);
        
-       video_control (add_label_to_sizer (_film_sizer, _film_panel, "Length"));
+       video_control (add_label_to_sizer (grid, _film_panel, _("Length")));
        _length = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
-       _film_sizer->Add (video_control (_length), 1, wxALIGN_CENTER_VERTICAL);
+       grid->Add (video_control (_length), 1, wxALIGN_CENTER_VERTICAL);
 
 
        {
-               video_control (add_label_to_sizer (_film_sizer, _film_panel, "Trim frames"));
+               video_control (add_label_to_sizer (grid, _film_panel, _("Trim frames")));
                wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-               video_control (add_label_to_sizer (s, _film_panel, "Start"));
-               _dcp_trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
-               s->Add (video_control (_dcp_trim_start));
-               video_control (add_label_to_sizer (s, _film_panel, "End"));
-               _dcp_trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
-               s->Add (video_control (_dcp_trim_end));
+               video_control (add_label_to_sizer (s, _film_panel, _("Start")));
+               _trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
+               s->Add (video_control (_trim_start));
+               video_control (add_label_to_sizer (s, _film_panel, _("End")));
+               _trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
+               s->Add (video_control (_trim_end));
 
-               _film_sizer->Add (s);
+               grid->Add (s);
        }
 
-       _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, wxT ("A/B"));
+       _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, _("A/B"));
        video_control (_dcp_ab);
-       _film_sizer->Add (_dcp_ab, 1);
-       _film_sizer->AddSpacer (0);
+       grid->Add (_dcp_ab, 1);
+       grid->AddSpacer (0);
 
        /* STILL-only stuff */
        {
-               still_control (add_label_to_sizer (_film_sizer, _film_panel, "Duration"));
+               still_control (add_label_to_sizer (grid, _film_panel, _("Duration")));
                wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                _still_duration = new wxSpinCtrl (_film_panel);
                still_control (_still_duration);
                s->Add (_still_duration, 1, wxEXPAND);
-               still_control (add_label_to_sizer (s, _film_panel, "s"));
-               _film_sizer->Add (s);
+               /* 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);
        }
 
        vector<DCPContentType const *> const ct = DCPContentType::all ();
@@ -189,8 +191,8 @@ FilmEditor::connect_to_widgets ()
        _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 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);
-       _dcp_trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_start_changed), 0, this);
-       _dcp_trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_end_changed), 0, this);
+       _trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_start_changed), 0, this);
+       _trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_end_changed), 0, this);
        _with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this);
        _subtitle_offset->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this);
        _subtitle_scale->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_scale_changed), 0, this);
@@ -216,72 +218,76 @@ void
 FilmEditor::make_video_panel ()
 {
        _video_panel = new wxPanel (_notebook);
-       _video_sizer = new wxFlexGridSizer (2, 4, 4);
-       wxBoxSizer* pad = new wxBoxSizer (wxVERTICAL);
-       pad->Add (_video_sizer, 0, wxALL, 8);
-       _video_panel->SetSizer (pad);
+       _video_sizer = new wxBoxSizer (wxVERTICAL);
+       _video_panel->SetSizer (_video_sizer);
+       
+       wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
+       _video_sizer->Add (grid, 0, wxALL, 8);
 
-       add_label_to_sizer (_video_sizer, _video_panel, "Format");
+       add_label_to_sizer (grid, _video_panel, _("Format"));
        _format = new wxChoice (_video_panel, wxID_ANY);
-       _video_sizer->Add (_format);
+       grid->Add (_format);
 
        {
-               add_label_to_sizer (_video_sizer, _video_panel, "Crop");
+               add_label_to_sizer (grid, _video_panel, _("Crop"));
                wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
 
-               add_label_to_sizer (s, _video_panel, "L");
+               /* TRANSLATORS: L, R, T and B are abbreviations for Left, Right, Top, Bottom, the four edges
+                  of the picture frame.
+               */
+               add_label_to_sizer (s, _video_panel, _("L"));
                _left_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
                s->Add (_left_crop, 0);
-               add_label_to_sizer (s, _video_panel, "R");
+               add_label_to_sizer (s, _video_panel, _("R"));
                _right_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
                s->Add (_right_crop, 0);
-               add_label_to_sizer (s, _video_panel, "T");
+               add_label_to_sizer (s, _video_panel, _("T"));
                _top_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
                s->Add (_top_crop, 0);
-               add_label_to_sizer (s, _video_panel, "B");
+               add_label_to_sizer (s, _video_panel, _("B"));
                _bottom_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
                s->Add (_bottom_crop, 0);
 
-               _video_sizer->Add (s);
+               grid->Add (s);
        }
 
        /* VIDEO-only stuff */
        {
-               video_control (add_label_to_sizer (_video_sizer, _video_panel, "Filters"));
+               video_control (add_label_to_sizer (grid, _video_panel, _("Filters")));
                wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-               _filters = new wxStaticText (_video_panel, wxID_ANY, wxT ("None"));
+               _filters = new wxStaticText (_video_panel, wxID_ANY, _("None"));
                video_control (_filters);
                s->Add (_filters, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM | wxRIGHT, 6);
-               _filters_button = new wxButton (_video_panel, wxID_ANY, wxT ("Edit..."));
+               _filters_button = new wxButton (_video_panel, wxID_ANY, _("Edit..."));
                video_control (_filters_button);
                s->Add (_filters_button, 0);
-               _video_sizer->Add (s, 1);
+               grid->Add (s, 1);
        }
 
-       video_control (add_label_to_sizer (_video_sizer, _video_panel, "Scaler"));
+       video_control (add_label_to_sizer (grid, _video_panel, _("Scaler")));
        _scaler = new wxChoice (_video_panel, wxID_ANY);
-       _video_sizer->Add (video_control (_scaler), 1);
+       grid->Add (video_control (_scaler), 1);
 
        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 (_video_sizer, _video_panel, "Colour look-up table");
+       add_label_to_sizer (grid, _video_panel, _("Colour look-up table"));
        _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);
-       _video_sizer->Add (_colour_lut, 1, wxEXPAND);
+       grid->Add (_colour_lut, 1, wxEXPAND);
 
        {
-               add_label_to_sizer (_video_sizer, _video_panel, "JPEG2000 bandwidth");
+               add_label_to_sizer (grid, _video_panel, _("JPEG2000 bandwidth"));
                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");
-               _video_sizer->Add (s, 1);
+               add_label_to_sizer (s, _video_panel, _("MBps"));
+               grid->Add (s, 1);
        }
 
        _left_crop->SetRange (0, 1024);
@@ -289,8 +295,8 @@ FilmEditor::make_video_panel ()
        _right_crop->SetRange (0, 1024);
        _bottom_crop->SetRange (0, 1024);
        _still_duration->SetRange (1, 60 * 60);
-       _dcp_trim_start->SetRange (0, 100);
-       _dcp_trim_end->SetRange (0, 100);
+       _trim_start->SetRange (0, 100);
+       _trim_end->SetRange (0, 100);
        _j2k_bandwidth->SetRange (50, 250);
 }
 
@@ -298,62 +304,67 @@ void
 FilmEditor::make_audio_panel ()
 {
        _audio_panel = new wxPanel (_notebook);
-       _audio_sizer = new wxFlexGridSizer (2, 4, 4);
-       wxBoxSizer* pad = new wxBoxSizer (wxVERTICAL);
-       pad->Add (_audio_sizer, 0, wxALL, 8);
-       _audio_panel->SetSizer (pad);
+       _audio_sizer = new wxBoxSizer (wxVERTICAL);
+       _audio_panel->SetSizer (_audio_sizer);
+       
+       wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
+       _audio_sizer->Add (grid, 0, wxALL, 8);
 
        {
-               video_control (add_label_to_sizer (_audio_sizer, _audio_panel, "Audio Gain"));
+               video_control (add_label_to_sizer (grid, _audio_panel, _("Audio Gain")));
                wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                _audio_gain = new wxSpinCtrl (_audio_panel);
                s->Add (video_control (_audio_gain), 1);
-               video_control (add_label_to_sizer (s, _audio_panel, "dB"));
+               video_control (add_label_to_sizer (s, _audio_panel, _("dB")));
                _audio_gain_calculate_button = new wxButton (_audio_panel, wxID_ANY, _("Calculate..."));
                video_control (_audio_gain_calculate_button);
                s->Add (_audio_gain_calculate_button, 1, wxEXPAND);
-               _audio_sizer->Add (s);
+               grid->Add (s);
        }
 
        {
-               video_control (add_label_to_sizer (_audio_sizer, _audio_panel, "Audio Delay"));
+               video_control (add_label_to_sizer (grid, _audio_panel, _("Audio Delay")));
                wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                _audio_delay = new wxSpinCtrl (_audio_panel);
                s->Add (video_control (_audio_delay), 1);
-               video_control (add_label_to_sizer (s, _audio_panel, "ms"));
-               _audio_sizer->Add (s);
+               /* TRANSLATORS: this is an abbreviation for milliseconds, the unit of time */
+               video_control (add_label_to_sizer (s, _audio_panel, _("ms")));
+               grid->Add (s);
        }
 
        {
                _use_content_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use content's audio"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
-               _audio_sizer->Add (video_control (_use_content_audio));
+               grid->Add (video_control (_use_content_audio));
                wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                _audio_stream = new wxChoice (_audio_panel, wxID_ANY);
                s->Add (video_control (_audio_stream), 1);
                _audio = new wxStaticText (_audio_panel, wxID_ANY, wxT (""));
                s->Add (video_control (_audio), 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 8);
-               _audio_sizer->Add (s, 1, wxEXPAND);
+               grid->Add (s, 1, wxEXPAND);
        }
 
        _use_external_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use external audio"));
-       _audio_sizer->Add (_use_external_audio);
-       _audio_sizer->AddSpacer (0);
+       grid->Add (_use_external_audio);
+       grid->AddSpacer (0);
 
        assert (MAX_AUDIO_CHANNELS == 6);
 
-       char const * channels[] = {
-               "Left",
-               "Right",
-               "Centre",
-               "Lfe (sub)",
-               "Left surround",
-               "Right surround"
+       /* TRANSLATORS: these are the names of audio channels; Lfe (sub) is the low-frequency
+          enhancement channel (sub-woofer)./
+       */
+       wxString const channels[] = {
+               _("Left"),
+               _("Right"),
+               _("Centre"),
+               _("Lfe (sub)"),
+               _("Left surround"),
+               _("Right surround"),
        };
 
        for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
-               add_label_to_sizer (_audio_sizer, _audio_panel, channels[i]);
-               _external_audio[i] = new wxFilePickerCtrl (_audio_panel, wxID_ANY, wxT (""), wxT ("Select Audio File"), wxT ("*.wav"));
-               _audio_sizer->Add (_external_audio[i], 1, wxEXPAND);
+               add_label_to_sizer (grid, _audio_panel, channels[i]);
+               _external_audio[i] = new wxFilePickerCtrl (_audio_panel, wxID_ANY, wxT (""), _("Select Audio File"), wxT ("*.wav"));
+               grid->Add (_external_audio[i], 1, wxEXPAND);
        }
 
        _audio_gain->SetRange (-60, 60);
@@ -364,29 +375,29 @@ void
 FilmEditor::make_subtitle_panel ()
 {
        _subtitle_panel = new wxPanel (_notebook);
-       _subtitle_sizer = new wxFlexGridSizer (2, 4, 4);
-       wxBoxSizer* pad = new wxBoxSizer (wxVERTICAL);
-       pad->Add (_subtitle_sizer, 0, wxALL, 8);
-       _subtitle_panel->SetSizer (pad);
+       _subtitle_sizer = new wxBoxSizer (wxVERTICAL);
+       _subtitle_panel->SetSizer (_subtitle_sizer);
+       wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
+       _subtitle_sizer->Add (grid, 0, wxALL, 8);
 
-       _with_subtitles = new wxCheckBox (_subtitle_panel, wxID_ANY, wxT("With Subtitles"));
+       _with_subtitles = new wxCheckBox (_subtitle_panel, wxID_ANY, _("With Subtitles"));
        video_control (_with_subtitles);
-       _subtitle_sizer->Add (_with_subtitles, 1);
+       grid->Add (_with_subtitles, 1);
        
        _subtitle_stream = new wxChoice (_subtitle_panel, wxID_ANY);
-       _subtitle_sizer->Add (video_control (_subtitle_stream));
+       grid->Add (video_control (_subtitle_stream));
 
-       video_control (add_label_to_sizer (_subtitle_sizer, _subtitle_panel, "Subtitle Offset"));
+       video_control (add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Offset")));
        _subtitle_offset = new wxSpinCtrl (_subtitle_panel);
-       _subtitle_sizer->Add (video_control (_subtitle_offset), 1);
+       grid->Add (video_control (_subtitle_offset), 1);
 
        {
-               video_control (add_label_to_sizer (_subtitle_sizer, _subtitle_panel, "Subtitle Scale"));
+               video_control (add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Scale")));
                wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                _subtitle_scale = new wxSpinCtrl (_subtitle_panel);
                s->Add (video_control (_subtitle_scale));
-               video_control (add_label_to_sizer (s, _subtitle_panel, "%"));
-               _subtitle_sizer->Add (s);
+               video_control (add_label_to_sizer (s, _subtitle_panel, _("%")));
+               grid->Add (s);
        }
 
        _subtitle_offset->SetRange (-1024, 1024);
@@ -449,7 +460,7 @@ FilmEditor::content_changed (wxCommandEvent &)
                _film->set_content (wx_to_std (_content->GetPath ()));
        } catch (std::exception& e) {
                _content->SetPath (std_to_wx (_film->directory ()));
-               error_dialog (this, String::compose ("Could not set content: %1", e.what ()));
+               error_dialog (this, wxString::Format (_("Could not set content: %s"), e.what ()));
        }
 }
 
@@ -620,10 +631,12 @@ FilmEditor::film_changed (Film::Property p)
                } 
                _length->SetLabel (std_to_wx (s.str ()));
                if (_film->length()) {
-                       _dcp_trim_start->SetRange (0, _film->length().get());
-                       _dcp_trim_end->SetRange (0, _film->length().get());
+                       _trim_start->SetRange (0, _film->length().get());
+                       _trim_end->SetRange (0, _film->length().get());
                }
                break;
+       case Film::DCP_INTRINSIC_DURATION:
+               break;
        case Film::DCP_CONTENT_TYPE:
                checked_set (_dcp_content_type, DCPContentType::as_index (_film->dcp_content_type ()));
                _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
@@ -634,11 +647,11 @@ FilmEditor::film_changed (Film::Property p)
        case Film::SCALER:
                checked_set (_scaler, Scaler::as_index (_film->scaler ()));
                break;
-       case Film::DCP_TRIM_START:
-               checked_set (_dcp_trim_start, _film->dcp_trim_start());
+       case Film::TRIM_START:
+               checked_set (_trim_start, _film->trim_start());
                break;
-       case Film::DCP_TRIM_END:
-               checked_set (_dcp_trim_end, _film->dcp_trim_end());
+       case Film::TRIM_END:
+               checked_set (_trim_end, _film->trim_end());
                break;
        case Film::AUDIO_GAIN:
                checked_set (_audio_gain, _film->audio_gain ());
@@ -761,8 +774,8 @@ FilmEditor::set_film (shared_ptr<Film> f)
        film_changed (Film::CROP);
        film_changed (Film::FILTERS);
        film_changed (Film::SCALER);
-       film_changed (Film::DCP_TRIM_START);
-       film_changed (Film::DCP_TRIM_END);
+       film_changed (Film::TRIM_START);
+       film_changed (Film::TRIM_END);
        film_changed (Film::DCP_AB);
        film_changed (Film::CONTENT_AUDIO_STREAM);
        film_changed (Film::EXTERNAL_AUDIO);
@@ -805,8 +818,8 @@ FilmEditor::set_things_sensitive (bool s)
        _scaler->Enable (s);
        _audio_stream->Enable (s);
        _dcp_content_type->Enable (s);
-       _dcp_trim_start->Enable (s);
-       _dcp_trim_end->Enable (s);
+       _trim_start->Enable (s);
+       _trim_end->Enable (s);
        _dcp_ab->Enable (s);
        _colour_lut->Enable (s);
        _j2k_bandwidth->Enable (s);
@@ -920,23 +933,23 @@ FilmEditor::still_duration_changed (wxCommandEvent &)
 }
 
 void
-FilmEditor::dcp_trim_start_changed (wxCommandEvent &)
+FilmEditor::trim_start_changed (wxCommandEvent &)
 {
        if (!_film) {
                return;
        }
 
-       _film->set_dcp_trim_start (_dcp_trim_start->GetValue ());
+       _film->set_trim_start (_trim_start->GetValue ());
 }
 
 void
-FilmEditor::dcp_trim_end_changed (wxCommandEvent &)
+FilmEditor::trim_end_changed (wxCommandEvent &)
 {
        if (!_film) {
                return;
        }
 
-       _film->set_dcp_trim_end (_dcp_trim_end->GetValue ());
+       _film->set_trim_end (_trim_end->GetValue ());
 }
 
 void
index 581ae3f69692ba6e7b0dc2d27b4bf9a6fa20df41..90be752d832a84d20611a9164016294bcab53607 100644 (file)
@@ -63,8 +63,8 @@ private:
        void content_changed (wxCommandEvent &);
        void trust_content_header_changed (wxCommandEvent &);
        void format_changed (wxCommandEvent &);
-       void dcp_trim_start_changed (wxCommandEvent &);
-       void dcp_trim_end_changed (wxCommandEvent &);
+       void trim_start_changed (wxCommandEvent &);
+       void trim_end_changed (wxCommandEvent &);
        void dcp_content_type_changed (wxCommandEvent &);
        void dcp_ab_toggled (wxCommandEvent &);
        void scaler_changed (wxCommandEvent &);
@@ -165,8 +165,8 @@ private:
        /** The Film's duration for still sources */
        wxSpinCtrl* _still_duration;
 
-       wxSpinCtrl* _dcp_trim_start;
-       wxSpinCtrl* _dcp_trim_end;
+       wxSpinCtrl* _trim_start;
+       wxSpinCtrl* _trim_end;
        /** Selector to generate an A/B comparison DCP */
        wxCheckBox* _dcp_ab;
 
index 16b3ccd9a7ac7c1219697cb6d59227acbdb4105c..4e5f3830060d6bd9d23067d5294ff352ba62cf59 100644 (file)
@@ -34,6 +34,7 @@
 #include "lib/scaler.h"
 #include "lib/exceptions.h"
 #include "lib/examine_content_job.h"
+#include "lib/filter.h"
 #include "film_viewer.h"
 #include "wx_util.h"
 #include "video_decoder.h"
@@ -44,6 +45,7 @@ using std::max;
 using std::cout;
 using std::list;
 using boost::shared_ptr;
+using libdcp::Size;
 
 FilmViewer::FilmViewer (shared_ptr<Film> f, wxWindow* p)
        : wxPanel (p)
@@ -95,10 +97,10 @@ FilmViewer::film_changed (Film::Property p)
                break;
        case Film::CONTENT:
        {
-               shared_ptr<DecodeOptions> o (new DecodeOptions);
-               o->decode_audio = false;
-               o->decode_subtitles = true;
-               o->video_sync = false;
+               DecodeOptions o;
+               o.decode_audio = false;
+               o.decode_subtitles = true;
+               o.video_sync = false;
                _decoders = decoder_factory (_film, o, 0);
                _decoders.video->Video.connect (bind (&FilmViewer::process_video, this, _1, _2, _3));
                _decoders.video->OutputChanged.connect (boost::bind (&FilmViewer::decoder_changed, this));
@@ -115,6 +117,7 @@ FilmViewer::film_changed (Film::Property p)
        case Film::SUBTITLE_OFFSET:
        case Film::SUBTITLE_SCALE:
        case Film::SCALER:
+       case Film::FILTERS:
                update_from_raw ();
                break;
        case Film::SUBTITLE_STREAM:
@@ -266,8 +269,15 @@ FilmViewer::raw_to_display ()
                old_size = _display_frame->size();
        }
 
+       boost::shared_ptr<Image> input = _raw_frame;
+
+       pair<string, string> const s = Filter::ffmpeg_strings (_film->filters());
+       if (!s.second.empty ()) {
+               input = input->post_process (s.second, true);
+       }
+       
        /* Get a compacted image as we have to feed it to wxWidgets */
-       _display_frame = _raw_frame->scale_and_convert_to_rgb (_film_size, 0, _film->scaler(), false);
+       _display_frame = input->scale_and_convert_to_rgb (_film_size, 0, _film->scaler(), false);
 
        if (old_size != _display_frame->size()) {
                _clear_required = true;
@@ -376,7 +386,9 @@ FilmViewer::get_frame ()
                        }
                }
        } catch (DecodeError& e) {
-               error_dialog (this, String::compose ("Could not decode video for view (%1)", e.what()));
+               _play_button->SetValue (false);
+               check_play_state ();
+               error_dialog (this, wxString::Format (_("Could not decode video for view (%s)"), e.what()));
        }
 }
 
index 8d9535d812c300bcfd2ff2f70848ed67af1bbf90..db6728ba58cd45a2ac05ed47148242ae72a89ab7 100644 (file)
@@ -37,13 +37,38 @@ FilterView::FilterView (wxWindow* parent, vector<Filter const *> const & active)
        
        vector<Filter const *> filters = Filter::all ();
 
+       typedef map<string, list<Filter const *> > CategoryMap;
+       CategoryMap categories;
+
        for (vector<Filter const *>::iterator i = filters.begin(); i != filters.end(); ++i) {
-               wxCheckBox* b = new wxCheckBox (this, wxID_ANY, std_to_wx ((*i)->name ()));
-               bool const a = find (active.begin(), active.end(), *i) != active.end ();
-               b->SetValue (a);
-               _filters[*i] = b;
-               b->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilterView::filter_toggled), 0, this);
-               sizer->Add (b);
+               CategoryMap::iterator j = categories.find ((*i)->category ());
+               if (j == categories.end ()) {
+                       list<Filter const *> c;
+                       c.push_back (*i);
+                       categories[(*i)->category()] = c;
+               } else {
+                       j->second.push_back (*i);
+               }
+       }
+
+       for (CategoryMap::iterator i = categories.begin(); i != categories.end(); ++i) {
+
+               wxStaticText* c = new wxStaticText (this, wxID_ANY, std_to_wx (i->first));
+               wxFont font = c->GetFont();
+               font.SetWeight(wxFONTWEIGHT_BOLD);
+               c->SetFont(font);
+               sizer->Add (c);
+
+               for (list<Filter const *>::iterator j = i->second.begin(); j != i->second.end(); ++j) {
+                       wxCheckBox* b = new wxCheckBox (this, wxID_ANY, std_to_wx ((*j)->name ()));
+                       bool const a = find (active.begin(), active.end(), *j) != active.end ();
+                       b->SetValue (a);
+                       _filters[*j] = b;
+                       b->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilterView::filter_toggled), 0, this);
+                       sizer->Add (b);
+               }
+
+               sizer->AddSpacer (6);
        }
 }
 
index 3f07faf062e4239835d692f8466d30c438fb7822..7f4b774c0eac82516a00d05d8d34215b9b42f9e2 100644 (file)
@@ -29,11 +29,11 @@ GainCalculatorDialog::GainCalculatorDialog (wxWindow* parent)
        wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6);
        table->AddGrowableCol (1, 1);
 
-       add_label_to_sizer (table, this, "I want to play this back at fader");
+       add_label_to_sizer (table, this, _("I want to play this back at fader"));
        _wanted = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, wxTextValidator (wxFILTER_NUMERIC));
        table->Add (_wanted, 1, wxEXPAND);
 
-       add_label_to_sizer (table, this, "But I have to use fader");
+       add_label_to_sizer (table, this, _("But I have to use fader"));
        _actual = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, wxTextValidator (wxFILTER_NUMERIC));
        table->Add (_actual, 1, wxEXPAND);
 
index 9c70405846cc1587baf8e3fcfd350a4bfd2267df..a5c02b16336ea636b9ce3b19fcfeb736270bf522 100644 (file)
@@ -95,7 +95,7 @@ JobManagerView::update ()
                                _job_records[*i].message->SetLabel (std_to_wx (st));
                                _job_records[*i].gauge->SetValue (p * 100);
                        } else {
-                               _job_records[*i].message->SetLabel (wxT ("Running"));
+                               _job_records[*i].message->SetLabel (_("Running"));
                                _job_records[*i].gauge->Pulse ();
                        }
                }
index f2056cf498abacd429c92464d9176082e3e7c5c0..cb02ecd0257dbddd771e80d131d2cc9f799d4a49 100644 (file)
@@ -35,8 +35,8 @@ JobWrapper::make_dcp (wxWindow* parent, shared_ptr<Film> film, bool transcode)
        try {
                film->make_dcp (transcode);
        } catch (BadSettingError& e) {
-               error_dialog (parent, String::compose ("Bad setting for %1 (%2)", e.setting(), e.what ()));
+               error_dialog (parent, wxString::Format (_("Bad setting for %s (%s)"), e.setting().c_str(), e.what()));
        } catch (std::exception& e) {
-               error_dialog (parent, String::compose ("Could not make DCP: %1", e.what ()));
+               error_dialog (parent, wxString::Format (_("Could not make DCP: %s"), e.what ()));
        }
 }
index eb6f2849b5cdd5ebc910b794bbff3ddcbb5bb6b9..90c2d727ef27defab61568d07025d7ec1408af76 100644 (file)
@@ -30,7 +30,7 @@ using namespace std;
 using namespace boost;
 
 NewFilmDialog::NewFilmDialog (wxWindow* parent)
-       : wxDialog (parent, wxID_ANY, wxString (_("New Film")))
+       : wxDialog (parent, wxID_ANY, _("New Film"))
 {
        wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
        SetSizer (overall_sizer);
@@ -39,11 +39,11 @@ NewFilmDialog::NewFilmDialog (wxWindow* parent)
        table->AddGrowableCol (1, 1);
        overall_sizer->Add (table, 1, wxEXPAND | wxALL, 6);
 
-       add_label_to_sizer (table, this, "Film name");
+       add_label_to_sizer (table, this, _("Film name"));
        _name = new wxTextCtrl (this, wxID_ANY);
        table->Add (_name, 1, wxEXPAND);
 
-       add_label_to_sizer (table, this, "Create in folder");
+       add_label_to_sizer (table, this, _("Create in folder"));
 #ifdef __WXMSW__
        _folder = new DirPickerCtrl (this);
 #else  
index b03c6b32c54d95e3b23cda98d63e121ee5338b29..338d0f972c7e7a104a872f95a3add26942a0d83d 100644 (file)
@@ -38,20 +38,20 @@ PropertiesDialog::PropertiesDialog (wxWindow* parent, shared_ptr<Film> film)
 {
        wxFlexGridSizer* table = new wxFlexGridSizer (2, 3, 6);
 
-       add_label_to_sizer (table, this, "Frames");
-       _frames = new wxStaticText (this, wxID_ANY, std_to_wx (""));
+       add_label_to_sizer (table, this, _("Frames"));
+       _frames = new wxStaticText (this, wxID_ANY, wxT (""));
        table->Add (_frames, 1, wxALIGN_CENTER_VERTICAL);
 
-       add_label_to_sizer (table, this, "Disk space required for frames");
-       _disk_for_frames = new wxStaticText (this, wxID_ANY, std_to_wx (""));
+       add_label_to_sizer (table, this, _("Disk space required for frames"));
+       _disk_for_frames = new wxStaticText (this, wxID_ANY, wxT (""));
        table->Add (_disk_for_frames, 1, wxALIGN_CENTER_VERTICAL);
        
-       add_label_to_sizer (table, this, "Total disk space required");
-       _total_disk = new wxStaticText (this, wxID_ANY, std_to_wx (""));
+       add_label_to_sizer (table, this, _("Total disk space required"));
+       _total_disk = new wxStaticText (this, wxID_ANY, wxT (""));
        table->Add (_total_disk, 1, wxALIGN_CENTER_VERTICAL);
 
-       add_label_to_sizer (table, this, "Frames already encoded");
-       _encoded = new ThreadedStaticText (this, "counting...", boost::bind (&PropertiesDialog::frames_already_encoded, this));
+       add_label_to_sizer (table, this, _("Frames already encoded"));
+       _encoded = new ThreadedStaticText (this, _("counting..."), boost::bind (&PropertiesDialog::frames_already_encoded, this));
        table->Add (_encoded, 1, wxALIGN_CENTER_VERTICAL);
 
        if (_film->length()) {
@@ -91,9 +91,9 @@ PropertiesDialog::frames_already_encoded () const
                return "";
        }
        
-       if (_film->dcp_length()) {
+       if (_film->length()) {
                /* XXX: encoded_frames() should check which frames have been encoded */
-               u << " (" << ((_film->encoded_frames() - _film->dcp_trim_start()) * 100 / _film->dcp_length().get()) << "%)";
+               u << " (" << (_film->encoded_frames() * 100 / _film->length().get()) << "%)";
        }
        return u.str ();
 }
index 7b394a484addd3a75114cd7fdb61c25b54a75285..1b5b71dc997784c7e2a927f0a7a010379c9cba86 100644 (file)
@@ -22,7 +22,7 @@
 #include "wx_util.h"
 
 ServerDialog::ServerDialog (wxWindow* parent, ServerDescription* server)
-       : wxDialog (parent, wxID_ANY, wxString (_("Server")))
+       : wxDialog (parent, wxID_ANY, _("Server"))
 {
        if (server) {
                _server = server;
@@ -33,11 +33,11 @@ ServerDialog::ServerDialog (wxWindow* parent, ServerDescription* server)
        wxFlexGridSizer* table = new wxFlexGridSizer (2, 4, 4);
        table->AddGrowableCol (1, 1);
 
-       add_label_to_sizer (table, this, "Host name or IP address");
+       add_label_to_sizer (table, this, _("Host name or IP address"));
        _host = new wxTextCtrl (this, wxID_ANY);
        table->Add (_host, 1, wxEXPAND);
 
-       add_label_to_sizer (table, this, "Threads to use");
+       add_label_to_sizer (table, this, _("Threads to use"));
        _threads = new wxSpinCtrl (this, wxID_ANY);
        table->Add (_threads, 1, wxEXPAND);
 
index 413071ea6989e0e395a5b5b64b23d2212f57dace..632dbc32e034b8ee39b8d507bfff97779a43b005 100644 (file)
@@ -36,9 +36,9 @@ using namespace boost;
  *  @param prop Proportion to pass when calling Add() on the wxSizer.
  */
 wxStaticText *
-add_label_to_sizer (wxSizer* s, wxWindow* p, string t, int prop)
+add_label_to_sizer (wxSizer* s, wxWindow* p, wxString t, int prop)
 {
-       wxStaticText* m = new wxStaticText (p, wxID_ANY, std_to_wx (t));
+       wxStaticText* m = new wxStaticText (p, wxID_ANY, t);
        s->Add (m, prop, wxALIGN_CENTER_VERTICAL | wxALL, 6);
        return m;
 }
@@ -48,9 +48,9 @@ add_label_to_sizer (wxSizer* s, wxWindow* p, string t, int prop)
  *  @param m Message.
  */
 void
-error_dialog (wxWindow* parent, string m)
+error_dialog (wxWindow* parent, wxString m)
 {
-       wxMessageDialog* d = new wxMessageDialog (parent, std_to_wx (m), wxT ("DVD-o-matic"), wxOK);
+       wxMessageDialog* d = new wxMessageDialog (parent, m, _("DVD-o-matic"), wxOK);
        d->ShowModal ();
        d->Destroy ();
 }
@@ -79,8 +79,8 @@ int const ThreadedStaticText::_update_event_id = 10000;
  *  @param initial Initial text for the wxStaticText while the computation is being run.
  *  @param fn Function which works out what the wxStaticText content should be and returns it.
  */
-ThreadedStaticText::ThreadedStaticText (wxWindow* parent, string initial, function<string ()> fn)
-       : wxStaticText (parent, wxID_ANY, std_to_wx (initial))
+ThreadedStaticText::ThreadedStaticText (wxWindow* parent, wxString initial, function<string ()> fn)
+       : wxStaticText (parent, wxID_ANY, initial)
 {
        Connect (_update_event_id, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (ThreadedStaticText::thread_finished), 0, this);
        _thread = new thread (bind (&ThreadedStaticText::run, this, fn));
index 0c77735eb2abf985317fedcaf110c145311a5f8c..dd069a9d745228c5e82024c5a9b94f6399b1589d 100644 (file)
@@ -28,8 +28,8 @@ class wxSpinCtrl;
  *  @brief Some utility functions and classes.
  */
 
-extern void error_dialog (wxWindow *, std::string);
-extern wxStaticText* add_label_to_sizer (wxSizer *, wxWindow *, std::string, int prop = 0);
+extern void error_dialog (wxWindow *, wxString);
+extern wxStaticText* add_label_to_sizer (wxSizer *, wxWindow *, wxString, int prop = 0);
 extern std::string wx_to_std (wxString);
 extern wxString std_to_wx (std::string);
 
@@ -41,7 +41,7 @@ extern wxString std_to_wx (std::string);
 class ThreadedStaticText : public wxStaticText
 {
 public:
-       ThreadedStaticText (wxWindow* parent, std::string initial, boost::function<std::string ()> fn);
+       ThreadedStaticText (wxWindow* parent, wxString initial, boost::function<std::string ()> fn);
        ~ThreadedStaticText ();
 
 private:
index 3aea4bcba86d4054fc40f018195602c7637dbfc5..ff0c2511803225b6bf951aeb9abc894b4ec0047f 100644 (file)
@@ -1,4 +1,4 @@
-version 1
+version 2
 name fred
 use_dci_name 1
 content 
@@ -12,8 +12,8 @@ bottom_crop 4
 filter pphb
 filter unsharp
 scaler bicubic
-dcp_trim_start 42
-dcp_trim_end 99
+trim_start 42
+trim_end 99
 dcp_ab 1
 use_content_audio 1
 audio_gain 0
@@ -34,6 +34,7 @@ package_type
 width 0
 height 0
 length 0
+dcp_intrinsic_duration 0
 content_digest 
 external_audio_stream external 0 0
 frames_per_second 0
index 5f6d687ac506cded67e844a386494333a580c430..9f1248f294dc0a92e07e78ec620eb7b1b2079bc9 100644 (file)
@@ -69,9 +69,69 @@ new_test_film (string name)
        return shared_ptr<Film> (new Film (d, false));
 }
 
-BOOST_AUTO_TEST_CASE (film_metadata_test)
+
+/* Check that Image::make_black works, and doesn't use values which crash
+   sws_scale().
+*/
+BOOST_AUTO_TEST_CASE (make_black_test)
 {
+       /* This needs to happen in the first test */
        dvdomatic_setup ();
+
+       libdcp::Size in_size (512, 512);
+       libdcp::Size out_size (1024, 1024);
+
+       {
+               /* Plain RGB input */
+               boost::shared_ptr<Image> foo (new SimpleImage (AV_PIX_FMT_RGB24, in_size, true));
+               foo->make_black ();
+               boost::shared_ptr<Image> bar = foo->scale_and_convert_to_rgb (out_size, 0, Scaler::from_id ("bicubic"), true);
+               
+               uint8_t* p = bar->data()[0];
+               for (int y = 0; y < bar->size().height; ++y) {
+                       uint8_t* q = p;
+                       for (int x = 0; x < bar->line_size()[0]; ++x) {
+                               BOOST_CHECK_EQUAL (*q++, 0);
+                       }
+                       p += bar->stride()[0];
+               }
+       }
+
+       {
+               /* YUV420P input */
+               boost::shared_ptr<Image> foo (new SimpleImage (AV_PIX_FMT_YUV420P, in_size, true));
+               foo->make_black ();
+               boost::shared_ptr<Image> bar = foo->scale_and_convert_to_rgb (out_size, 0, Scaler::from_id ("bicubic"), true);
+               
+               uint8_t* p = bar->data()[0];
+               for (int y = 0; y < bar->size().height; ++y) {
+                       uint8_t* q = p;
+                       for (int x = 0; x < bar->line_size()[0]; ++x) {
+                               BOOST_CHECK_EQUAL (*q++, 0);
+                       }
+                       p += bar->stride()[0];
+               }
+       }
+
+       {
+               /* YUV422P10LE input */
+               boost::shared_ptr<Image> foo (new SimpleImage (AV_PIX_FMT_YUV422P10LE, in_size, true));
+               foo->make_black ();
+               boost::shared_ptr<Image> bar = foo->scale_and_convert_to_rgb (out_size, 0, Scaler::from_id ("bicubic"), true);
+               
+               uint8_t* p = bar->data()[0];
+               for (int y = 0; y < bar->size().height; ++y) {
+                       uint8_t* q = p;
+                       for (int x = 0; x < bar->line_size()[0]; ++x) {
+                               BOOST_CHECK_EQUAL (*q++, 0);
+                       }
+                       p += bar->stride()[0];
+               }
+       }
+}
+
+BOOST_AUTO_TEST_CASE (film_metadata_test)
+{
        setup_test_config ();
 
        string const test_film = "build/test/film_metadata_test";
@@ -99,8 +159,8 @@ BOOST_AUTO_TEST_CASE (film_metadata_test)
        f_filters.push_back (Filter::from_id ("pphb"));
        f_filters.push_back (Filter::from_id ("unsharp"));
        f->set_filters (f_filters);
-       f->set_dcp_trim_start (42);
-       f->set_dcp_trim_end (99);
+       f->set_trim_start (42);
+       f->set_trim_end (99);
        f->set_dcp_ab (true);
        f->write_metadata ();
 
@@ -121,8 +181,8 @@ BOOST_AUTO_TEST_CASE (film_metadata_test)
        BOOST_CHECK_EQUAL (g_filters.size(), 2);
        BOOST_CHECK_EQUAL (g_filters.front(), Filter::from_id ("pphb"));
        BOOST_CHECK_EQUAL (g_filters.back(), Filter::from_id ("unsharp"));
-       BOOST_CHECK_EQUAL (g->dcp_trim_start(), 42);
-       BOOST_CHECK_EQUAL (g->dcp_trim_end(), 99);
+       BOOST_CHECK_EQUAL (g->trim_start(), 42);
+       BOOST_CHECK_EQUAL (g->trim_end(), 99);
        BOOST_CHECK_EQUAL (g->dcp_ab(), true);
        
        g->write_metadata ();
@@ -325,26 +385,30 @@ do_remote_encode (shared_ptr<DCPVideoFrame> frame, ServerDescription* descriptio
 
 BOOST_AUTO_TEST_CASE (client_server_test)
 {
-       shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGB24, libdcp::Size (1998, 1080), false));
+       shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGB24, libdcp::Size (1998, 1080), true));
        uint8_t* p = image->data()[0];
        
        for (int y = 0; y < 1080; ++y) {
+               uint8_t* q = p;
                for (int x = 0; x < 1998; ++x) {
-                       *p++ = x % 256;
-                       *p++ = y % 256;
-                       *p++ = (x + y) % 256;
+                       *q++ = x % 256;
+                       *q++ = y % 256;
+                       *q++ = (x + y) % 256;
                }
+               p += image->stride()[0];
        }
 
-       shared_ptr<Image> sub_image (new SimpleImage (PIX_FMT_RGBA, libdcp::Size (100, 200), false));
+       shared_ptr<Image> sub_image (new SimpleImage (PIX_FMT_RGBA, libdcp::Size (100, 200), true));
        p = sub_image->data()[0];
        for (int y = 0; y < 200; ++y) {
+               uint8_t* q = p;
                for (int x = 0; x < 100; ++x) {
-                       *p++ = y % 256;
-                       *p++ = x % 256;
-                       *p++ = (x + y) % 256;
-                       *p++ = 1;
+                       *q++ = y % 256;
+                       *q++ = x % 256;
+                       *q++ = (x + y) % 256;
+                       *q++ = 1;
                }
+               p += sub_image->stride()[0];
        }
 
        shared_ptr<Subtitle> subtitle (new Subtitle (Position (50, 60), sub_image));
@@ -419,7 +483,7 @@ BOOST_AUTO_TEST_CASE (make_dcp_with_range_test)
        film->examine_content ();
        film->set_format (Format::from_nickname ("Flat"));
        film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
-       film->set_dcp_trim_end (42);
+       film->set_trim_end (42);
        film->make_dcp (true);
 
        while (JobManager::instance()->work_to_do() && !JobManager::instance()->errors()) {
@@ -429,6 +493,111 @@ 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)
+{
+       /* Run some tests with a limited range of allowed rates */
+       
+       std::list<int> afr;
+       afr.push_back (24);
+       afr.push_back (25);
+       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);
+       
+       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);
+       
+       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);
+       
+       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);
+
+       /* Now add some more rates and see if it will use them
+          in preference to skip/repeat.
+       */
+
+       afr.push_back (48);
+       afr.push_back (50);
+       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);
+       
+       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);
+}
+
 BOOST_AUTO_TEST_CASE (audio_sampling_rate_test)
 {
        shared_ptr<Film> f = new_test_film ("audio_sampling_rate_test");
index 7b34dfea61d47a29741e0171c5809196bbf9b2a0..7669ece9d0aab9dbf82bc7f0415adc450eac2a06 100644 (file)
@@ -93,7 +93,7 @@ CreateShortCut "$DESKTOP\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" ""
 CreateShortCut "$DESKTOP\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" ""
  
 CreateDirectory "$SMPROGRAMS\DVD-o-matic"
-CreateShortCut "$SMPROGRAMS\DVD-o-matic\Uninstall.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0
+CreateShortCut "$SMPROGRAMS\DVD-o-matic\Uninstall DVD-o-matic.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0
 CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" "" "$INSTDIR\bin\dvdomatic.exe" 0
 CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" "" "$INSTDIR\bin\servomatic_gui.exe" 0
  
index d4fa9965918f7e5a9410fc1622a2d3c5b602a51b..4f8d7ec48f339d4825d7591fee6ab6038cc3ae83 100644 (file)
@@ -103,7 +103,7 @@ CreateShortCut "$DESKTOP\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" ""
 CreateShortCut "$DESKTOP\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" ""
  
 CreateDirectory "$SMPROGRAMS\DVD-o-matic"
-CreateShortCut "$SMPROGRAMS\DVD-o-matic\Uninstall.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0
+CreateShortCut "$SMPROGRAMS\DVD-o-matic\Uninstall DVD-o-matic.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0
 CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" "" "$INSTDIR\bin\dvdomatic.exe" 0
 CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" "" "$INSTDIR\bin\servomatic_gui.exe" 0
  
diff --git a/wscript b/wscript
index 43ac63548e31b936ae98a0e7248dcc351df6816c..4bcb0bd11681c1cd14e889506205de391859d192 100644 (file)
--- a/wscript
+++ b/wscript
@@ -52,7 +52,7 @@ def configure(conf):
         conf.env.append_value('CXXFLAGS', '-O2')
 
     if not conf.options.static:
-        conf.check_cfg(package = 'libdcp', atleast_version = '0.36', args = '--cflags --libs', uselib_store = 'DCP', mandatory = True)
+        conf.check_cfg(package = 'libdcp', atleast_version = '0.39', args = '--cflags --libs', uselib_store = 'DCP', mandatory = True)
         conf.check_cfg(package = 'libavformat', args = '--cflags --libs', uselib_store = 'AVFORMAT', mandatory = True)
         conf.check_cfg(package = 'libavfilter', args = '--cflags --libs', uselib_store = 'AVFILTER', mandatory = True)
         conf.check_cfg(package = 'libavcodec', args = '--cflags --libs', uselib_store = 'AVCODEC', mandatory = True)
@@ -91,7 +91,8 @@ def configure(conf):
 
     conf.check_cfg(package = 'sndfile', args = '--cflags --libs', uselib_store = 'SNDFILE', mandatory = True)
     conf.check_cfg(package = 'glib-2.0', args = '--cflags --libs', uselib_store = 'GLIB', mandatory = True)
-    conf.check_cfg(package = 'liblzma', args = '--cflags --libs', uselib_store = 'LZMA', mandatory = True)
+    if conf.options.target_windows is False:
+        conf.check_cfg(package = 'liblzma', args = '--cflags --libs', uselib_store = 'LZMA', mandatory = True)
     conf.check_cfg(package = '', path = conf.options.magickpp_config, args = '--cppflags --cxxflags --libs', uselib_store = 'MAGICK', mandatory = True)
 
     if conf.options.static:
@@ -108,6 +109,18 @@ def configure(conf):
         conf.check_cfg(package = 'libopenjpeg', args = '--cflags --libs', atleast_version = '1.5.0', uselib_store = 'OPENJPEG', mandatory = True)
         conf.check_cfg(package = 'libopenjpeg', args = '--cflags --libs', max_version = '1.5.1', mandatory = True)
 
+    conf.check_cxx(fragment = """
+                              #include <boost/version.hpp>\n
+                              #if BOOST_VERSION < 104500\n
+                              #error boost too old\n
+                              #endif\n
+                              int main(void) { return 0; }\n
+                              """,
+                   mandatory = True,
+                   msg = 'Checking for boost library >= 1.45',
+                   okmsg = 'yes',
+                   errmsg = 'too old\nPlease install boost version 1.45 or higher.')
+
     conf.check_cc(fragment  = """
                               #include <libssh/libssh.h>\n
                               int main () {\n
@@ -120,6 +133,7 @@ def configure(conf):
                              #include <boost/thread.hpp>\n
                              int main() { boost::thread t (); }\n
                              """, msg = 'Checking for boost threading library',
+                             libpath = '/usr/local/lib',
                               lib = [boost_thread, 'boost_system%s' % boost_lib_suffix],
                               uselib_store = 'BOOST_THREAD')