Merge master branch.
authorCarl Hetherington <cth@carlh.net>
Wed, 23 Jan 2013 20:15:13 +0000 (20:15 +0000)
committerCarl Hetherington <cth@carlh.net>
Wed, 23 Jan 2013 20:15:13 +0000 (20:15 +0000)
14 files changed:
1  2 
src/lib/dcp_video_frame.h
src/lib/ffmpeg_decoder.cc
src/lib/film.cc
src/lib/film.h
src/lib/filter_graph.cc
src/lib/format.cc
src/lib/image.cc
src/lib/imagemagick_decoder.cc
src/lib/server.cc
src/lib/subtitle.cc
src/lib/util.cc
src/wx/film_editor.cc
src/wx/film_editor.h
src/wx/film_viewer.cc

index 1d434505abef04a1c7efee3d8e946ca2d07b7a26,c0eff3f358a2581565266c4b37695577e1831a76..e988b663a80493158b552a959d8b8ec09010fea3
@@@ -26,7 -26,7 +26,7 @@@
   */
  
  class FilmState;
 -class EncodeOptions;
 +class Film;
  class ServerDescription;
  class Scaler;
  class Image;
@@@ -39,17 -39,18 +39,16 @@@ class Subtitle
  class EncodedData
  {
  public:
-       /** @param s Size of data, in bytes.
 -      /** @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_hash (boost::shared_ptr<const Film>, int) const;
  
        /** @return data */
        uint8_t* data () const {
  protected:
        uint8_t* _data; ///< data
        int _size;      ///< data size in bytes
 +
 +private:
 +      /* No copy construction */
 +      EncodedData (EncodedData const &);
  };
  
  /** @class LocallyEncodedData
  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
@@@ -92,6 -91,7 +91,6 @@@ class RemotelyEncodedData : public Enco
  {
  public:
        RemotelyEncodedData (int s);
 -      ~RemotelyEncodedData ();
  };
  
  /** @class DCPVideoFrame
@@@ -108,7 -108,7 +107,7 @@@ class DCPVideoFram
  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 ();
        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 +124,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 e7dfc206be2dfe6c105e374e65472f293cb38a5e,aff3ff666d0ce38cb689565d399cec8034cdcde4..81f40564460a035856332ae46f25cacf14d1a9dd
@@@ -58,9 -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)
@@@ -79,7 -78,7 +79,7 @@@
        setup_audio ();
        setup_subtitle ();
  
 -      if (!o->video_sync) {
 +      if (!o.video_sync) {
                _first_video = 0;
        }
  }
@@@ -240,7 -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
                                _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) {
                        }
                }
                        
 -      } 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;
@@@ -469,10 -468,10 +469,10 @@@ FFmpegDecoder::audio_sample_format () c
        return _audio_codec_context->sample_fmt;
  }
  
- Size
libdcp::Size
  FFmpegDecoder::native_size () const
  {
-       return Size (_video_codec_context->width, _video_codec_context->height);
+       return libdcp::Size (_video_codec_context->width, _video_codec_context->height);
  }
  
  PixelFormat
@@@ -559,12 -558,12 +559,12 @@@ FFmpegDecoder::filter_and_emit_video (A
        shared_ptr<FilterGraph> graph;
  
        list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin();
-       while (i != _filter_graphs.end() && !(*i)->can_process (Size (frame->width, frame->height), (AVPixelFormat) frame->format)) {
+       while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (frame->width, frame->height), (AVPixelFormat) frame->format)) {
                ++i;
        }
  
        if (i == _filter_graphs.end ()) {
-               graph.reset (new FilterGraph (_film, this, Size (frame->width, frame->height), (AVPixelFormat) frame->format));
+               graph.reset (new FilterGraph (_film, this, libdcp::Size (frame->width, frame->height), (AVPixelFormat) frame->format));
                _filter_graphs.push_back (graph);
                _film->log()->log (String::compose ("New graph for %1x%2, pixel format %3", frame->width, frame->height, frame->format));
        } else {
diff --combined src/lib/film.cc
index c91a8047133dc0e14a1ff0cc47f0a946ada72e9e,5a11b0ca969ec5bfb262168158fb3be52e52514f..ae9edbfdb0a93a954a6e7c13bc967738598430e4
@@@ -39,6 -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"
@@@ -71,9 -72,8 +71,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 -89,8 +89,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)
@@@ -155,9 -155,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)
-       , _reel_size         (o._reel_size)
        , _dcp_ab            (o._dcp_ab)
        , _content_audio_stream (o._content_audio_stream)
        , _external_audio    (o._external_audio)
        , _package_type      (o._package_type)
        , _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)
@@@ -256,9 -254,7 +255,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()));
                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)));
 +      // r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), od, r)));
  }
  
  /** Start a job to examine our content file */
@@@ -388,11 -411,8 +387,8 @@@ Film::write_metadata () cons
                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";
-       if (_reel_size) {
-               f << "reel_size " << _reel_size.get() << "\n";
-       }
        f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
        if (_content_audio_stream) {
                f << "selected_content_audio_stream " << _content_audio_stream->to_string() << "\n";
        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) {
@@@ -504,12 -523,10 +500,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 == "reel_size") {
-                       _reel_size = boost::lexical_cast<uint64_t> (v);
                } else if (k == "dcp_ab") {
                        _dcp_ab = (v == "1");
                } else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) {
                        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")) {
        _dirty = false;
  }
  
- Size
- Film::cropped_size (Size s) const
libdcp::Size
+ Film::cropped_size (libdcp::Size s) const
  {
        boost::mutex::scoped_lock lm (_state_mutex);
        s.width -= _crop.left + _crop.right;
@@@ -686,25 -698,30 +680,25 @@@ Film::target_audio_sample_rate () cons
        /* 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 */
@@@ -876,7 -893,8 +870,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 ());
@@@ -1051,45 -1069,25 +1045,25 @@@ 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
- Film::set_reel_size (uint64_t s)
- {
-       {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               _reel_size = s;
-       }
-       signal_changed (REEL_SIZE);
- }
- void
- Film::unset_reel_size ()
- {
-       {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               _reel_size = boost::optional<uint64_t> ();
-       }
-       signal_changed (REEL_SIZE);
- }
  void
  Film::set_dcp_ab (bool a)
  {
@@@ -1118,7 -1116,8 +1092,7 @@@ Film::set_external_audio (vector<string
                _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 ();
        }
@@@ -1298,7 -1297,7 +1272,7 @@@ Film::set_package_type (string p
  }
  
  void
- Film::set_size (Size s)
+ Film::set_size (libdcp::Size s)
  {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
@@@ -1325,17 -1324,7 +1299,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)
@@@ -1416,45 -1405,3 +1390,45 @@@ Film::audio_stream () cons
  
        return _external_audio_stream;
  }
 +
 +/** @param f DCP frame index.
 + *  @param t true to return a temporary file path, otherwise a permanent one.
 + *  @return The path to write this video frame to.
 + */
 +string
 +Film::frame_out_path (int f, bool t) const
 +{
 +      stringstream s;
 +      s << j2k_dir() << "/";
 +      s.width (8);
 +      s << std::setfill('0') << f << ".j2c";
 +
 +      if (t) {
 +              s << ".tmp";
 +      }
 +
 +      return s.str ();
 +}
 +
 +string
 +Film::hash_out_path (int f, bool t) const
 +{
 +      return frame_out_path (f, t) + ".md5";
 +}
 +
 +/** @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.
 + */
 +string
 +Film::multichannel_audio_out_path (int c, bool t) const
 +{
 +      stringstream s;
 +      s << dir ("wavs") << "/" << (c + 1) << ".wav";
 +      if (t) {
 +              s << ".tmp";
 +      }
 +      
 +      return s.str ();
 +}
 +
diff --combined src/lib/film.h
index e10abfa4bbed2127e14627a4282786b1c9e3dc3f,d3530b81772ac43f117d2d4f2734778c9d0d28c9..60646b0c8d78adc52f6fa8da0740d704926ac1c9
@@@ -78,10 -78,6 +78,10 @@@ public
        std::string file (std::string f) const;
        std::string dir (std::string d) const;
  
 +      std::string frame_out_path (int f, bool t) const;
 +      std::string hash_out_path (int f, bool t) const;
 +      std::string multichannel_audio_out_path (int c, bool t) const;
 +      
        std::string content_path () const;
        ContentType content_type () const;
        
        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;
                CROP,
                FILTERS,
                SCALER,
 -              DCP_TRIM_START,
 -              DCP_TRIM_END,
 +              TRIM_START,
 +              TRIM_END,
-               REEL_SIZE,
                DCP_AB,
                CONTENT_AUDIO_STREAM,
                EXTERNAL_AUDIO,
                DCI_METADATA,
                SIZE,
                LENGTH,
 +              DCP_INTRINSIC_DURATION,
                CONTENT_AUDIO_STREAMS,
                SUBTITLE_STREAMS,
                FRAMES_PER_SECOND,
                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;
        }
  
-       boost::optional<uint64_t> reel_size () const {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               return _reel_size;
-       }
-       
        bool dcp_ab () const {
                boost::mutex::scoped_lock lm (_state_mutex);
                return _dcp_ab;
                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;
        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_reel_size (uint64_t);
-       void unset_reel_size ();
        void set_dcp_ab (bool);
        void set_content_audio_stream (boost::shared_ptr<AudioStream>);
        void set_external_audio (std::vector<std::string>);
        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> >);
@@@ -453,11 -434,9 +445,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;
-       /** Approximate target reel size in bytes; if not set, use a single reel */
-       boost::optional<uint64_t> _reel_size;
        /** 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.
  
        /* 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 */
diff --combined src/lib/filter_graph.cc
index 86864a762295c93c4d22085570d45feb5b7f6640,6cd7dc2cbb52f38e8f9eb9db971a1f8d4bdc2d7e..3a13d93d069c5f7b0bd84545e4a560089056092f
@@@ -47,7 -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.
@@@ -55,7 -54,7 +55,7 @@@
   *  @param s Size of the images to process.
   *  @param p Pixel format of the images to process.
   */
- FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, Size s, AVPixelFormat p)
+ FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p)
        : _buffer_src_context (0)
        , _buffer_sink_context (0)
        , _size (s)
@@@ -206,7 -205,7 +206,7 @@@ FilterGraph::process (AVFrame const * f
   *  @return true if this chain can process images with `s' and `p', otherwise false.
   */
  bool
- FilterGraph::can_process (Size s, AVPixelFormat p) const
+ FilterGraph::can_process (libdcp::Size s, AVPixelFormat p) const
  {
        return (_size == s && _pixel_format == p);
  }
diff --combined src/lib/format.cc
index a80fab6193d0998fa320f8b745de70631a00105e,088a16059d7f68ac4432f029afacd0ab0b966cb5..4583dd0e58602840c95cb5b1724c5ab557a3fb6a
@@@ -35,7 -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;
  
@@@ -68,19 -67,19 +68,19 @@@ Format::as_metadata () cons
  void
  Format::setup_formats ()
  {
-       _formats.push_back (new FixedFormat (119, Size (1285, 1080), "119", "1.19", "F"));
-       _formats.push_back (new FixedFormat (133, Size (1436, 1080), "133", "1.33", "F"));
-       _formats.push_back (new FixedFormat (138, Size (1485, 1080), "138", "1.375", "F"));
-       _formats.push_back (new FixedFormat (133, Size (1998, 1080), "133-in-flat", "4:3 within Flat", "F"));
-       _formats.push_back (new FixedFormat (137, Size (1480, 1080), "137", "Academy", "F"));
-       _formats.push_back (new FixedFormat (166, Size (1793, 1080), "166", "1.66", "F"));
-       _formats.push_back (new FixedFormat (166, Size (1998, 1080), "166-in-flat", "1.66 within Flat", "F"));
-       _formats.push_back (new FixedFormat (178, Size (1998, 1080), "178-in-flat", "16:9 within Flat", "F"));
-       _formats.push_back (new FixedFormat (178, Size (1920, 1080), "178", "16:9", "F"));
-       _formats.push_back (new FixedFormat (185, Size (1998, 1080), "185", "Flat", "F"));
-       _formats.push_back (new FixedFormat (239, Size (2048, 858), "239", "Scope", "S"));
-       _formats.push_back (new VariableFormat (Size (1998, 1080), "var-185", "Flat", "F"));
-       _formats.push_back (new VariableFormat (Size (2048, 858), "var-239", "Scope", "S"));
+       _formats.push_back (new FixedFormat (119, libdcp::Size (1285, 1080), "119", "1.19", "F"));
+       _formats.push_back (new FixedFormat (133, libdcp::Size (1436, 1080), "133", "1.33", "F"));
+       _formats.push_back (new FixedFormat (138, libdcp::Size (1485, 1080), "138", "1.375", "F"));
+       _formats.push_back (new FixedFormat (133, libdcp::Size (1998, 1080), "133-in-flat", "4:3 within Flat", "F"));
+       _formats.push_back (new FixedFormat (137, libdcp::Size (1480, 1080), "137", "Academy", "F"));
+       _formats.push_back (new FixedFormat (166, libdcp::Size (1793, 1080), "166", "1.66", "F"));
+       _formats.push_back (new FixedFormat (166, libdcp::Size (1998, 1080), "166-in-flat", "1.66 within Flat", "F"));
+       _formats.push_back (new FixedFormat (178, libdcp::Size (1998, 1080), "178-in-flat", "16:9 within Flat", "F"));
+       _formats.push_back (new FixedFormat (178, libdcp::Size (1920, 1080), "178", "16:9", "F"));
+       _formats.push_back (new FixedFormat (185, libdcp::Size (1998, 1080), "185", "Flat", "F"));
+       _formats.push_back (new FixedFormat (239, libdcp::Size (2048, 858), "239", "Scope", "S"));
+       _formats.push_back (new VariableFormat (libdcp::Size (1998, 1080), "var-185", "Flat", "F"));
+       _formats.push_back (new VariableFormat (libdcp::Size (2048, 858), "var-239", "Scope", "S"));
  }
  
  /** @param n Nickname.
@@@ -141,7 -140,7 +141,7 @@@ Format::all (
   *  @param id ID (e.g. 185)
   *  @param n Nick name (e.g. Flat)
   */
- FixedFormat::FixedFormat (int r, Size dcp, string id, string n, string d)
+ FixedFormat::FixedFormat (int r, libdcp::Size dcp, string id, string n, string d)
        : Format (dcp, id, n, d)
        , _ratio (r)
  {
@@@ -161,7 -160,7 +161,7 @@@ Format::dcp_padding (shared_ptr<const F
        return p;
  }
  
- VariableFormat::VariableFormat (Size dcp, string id, string n, string d)
+ VariableFormat::VariableFormat (libdcp::Size dcp, string id, string n, string d)
        : Format (dcp, id, n, d)
  {
  
diff --combined src/lib/image.cc
index 0a51add002cf7787c919a9c29a03e36237a72200,feda09ec578bb41baeda31f033c7b770d650a45a..9223fdc5d0019cdfecbe2a2e20930c8546a7c426
@@@ -42,7 -42,6 +42,7 @@@ extern "C" 
  
  using namespace std;
  using namespace boost;
 +using libdcp::Size;
  
  void
  Image::swap (Image& other)
@@@ -96,15 -95,11 +96,15 @@@ Image::components () cons
  }
  
  shared_ptr<Image>
- Image::scale (Size out_size, Scaler const * scaler, bool result_aligned) const
 -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(),
   *  @param scaler Scaler to use.
   */
  shared_ptr<Image>
- Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scaler, bool result_aligned) const
 -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 ());
  
-       Size content_size = out_size;
+       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(),
           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
@@@ -224,7 -215,7 +224,7 @@@ Image::post_process (string pp, bool al
  shared_ptr<Image>
  Image::crop (Crop crop, bool aligned) const
  {
-       Size cropped_size = size ();
+       libdcp::Size cropped_size = size ();
        cropped_size.width -= crop.left + crop.right;
        cropped_size.height -= crop.top + crop.bottom;
  
@@@ -253,25 -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;
@@@ -370,7 -349,7 +370,7 @@@ Image::bytes_per_pixel (int c) cons
                        return 0.5;
                }
        case PIX_FMT_YUV422P10LE:
 -              if (c == 1) {
 +              if (c == 0) {
                        return 2;
                } else {
                        return 1;
   *  @param p Pixel format.
   *  @param s Size in pixels.
   */
- SimpleImage::SimpleImage (AVPixelFormat p, Size s, bool aligned)
+ SimpleImage::SimpleImage (AVPixelFormat p, libdcp::Size s, bool aligned)
        : Image (p)
        , _size (s)
        , _aligned (aligned)
@@@ -487,18 -466,12 +487,18 @@@ SimpleImage::stride () cons
        return _stride;
  }
  
- Size
libdcp::Size
  SimpleImage::size () const
  {
        return _size;
  }
  
 +bool
 +SimpleImage::aligned () const
 +{
 +      return _aligned;
 +}
 +
  FilterBufferImage::FilterBufferImage (AVPixelFormat p, AVFilterBufferRef* b)
        : Image (p)
        , _buffer (b)
@@@ -530,19 -503,12 +530,19 @@@ FilterBufferImage::stride () cons
        return _buffer->linesize;
  }
  
- Size
libdcp::Size
  FilterBufferImage::size () const
  {
-       return Size (_buffer->video->w, _buffer->video->h);
+       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 a623fd226eaaf0c6544c772862d530d047b11a5a,5ebd6c8e1ccaba8ba498b6d453ad127573d6c0c3..99b9e1d340c47e5bfe8784b568ec486da754bb87
  
  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)
  {
@@@ -51,7 -50,7 +51,7 @@@
        _iter = _files.begin ();
  }
  
- Size
libdcp::Size
  ImageMagickDecoder::native_size () const
  {
        if (_files.empty ()) {
@@@ -61,7 -60,7 +61,7 @@@
        /* Look at the first file and assume its size holds for all */
        using namespace MagickCore;
        Magick::Image* image = new Magick::Image (_film->content_path ());
-       Size const s = Size (image->columns(), image->rows());
+       libdcp::Size const s = libdcp::Size (image->columns(), image->rows());
        delete image;
  
        return s;
@@@ -71,7 -70,7 +71,7 @@@ boo
  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;
                }
  
@@@ -81,7 -80,7 +81,7 @@@
        
        Magick::Image* magick_image = new Magick::Image (_film->content_path ());
        
-       Size size = native_size ();
+       libdcp::Size size = native_size ();
        shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGB24, size, false));
  
        using namespace MagickCore;
@@@ -98,7 -97,7 +98,7 @@@
  
        delete magick_image;
  
 -      image = image->crop (_film->crop(), false);
 +      image = image->crop (_film->crop(), true);
        
        emit_video (image, 0);
  
diff --combined src/lib/server.cc
index 134cb65a0140a217ccd4b9a10d3d8a0b7094627d,1bb8f205ed65e734606d82196cacd3cf91ae115e..d75ab0fb679d05f894638158c8ec16db79d63153
@@@ -45,7 -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.
@@@ -93,9 -92,9 +93,9 @@@ Server::process (shared_ptr<Socket> soc
                return -1;
        }
  
-       Size in_size (get_required_int (kv, "input_width"), get_required_int (kv, "input_height"));
+       libdcp::Size in_size (get_required_int (kv, "input_width"), get_required_int (kv, "input_height"));
        int pixel_format_int = get_required_int (kv, "input_pixel_format");
-       Size out_size (get_required_int (kv, "output_width"), get_required_int (kv, "output_height"));
+       libdcp::Size out_size (get_required_int (kv, "output_width"), get_required_int (kv, "output_height"));
        int padding = get_required_int (kv, "padding");
        int subtitle_offset = get_required_int (kv, "subtitle_offset");
        float subtitle_scale = get_required_float (kv, "subtitle_scale");
        int colour_lut_index = get_required_int (kv, "colour_lut");
        int j2k_bandwidth = get_required_int (kv, "j2k_bandwidth");
        Position subtitle_position (get_optional_int (kv, "subtitle_x"), get_optional_int (kv, "subtitle_y"));
-       Size subtitle_size (get_optional_int (kv, "subtitle_width"), get_optional_int (kv, "subtitle_height"));
+       libdcp::Size subtitle_size (get_optional_int (kv, "subtitle_width"), get_optional_int (kv, "subtitle_height"));
  
        /* This checks that colour_lut_index is within range */
        colour_lut_index_to_name (colour_lut_index);
diff --combined src/lib/subtitle.cc
index a7aa7cd21789a772edf04be8edd296362140b776,8a9998d6a9394501c7a5b489ae2bef755a86a099..3754e5acf012c9a8dfe921fab46bb02440f397ef
@@@ -27,7 -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.
@@@ -56,7 -55,7 +56,7 @@@ TimedSubtitle::TimedSubtitle (AVSubtitl
                throw DecodeError ("non-bitmap subtitles not yet supported");
        }
        
-       shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGBA, Size (rect->w, rect->h), true));
+       shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGBA, libdcp::Size (rect->w, rect->h), true));
  
        /* Start of the first line in the subtitle */
        uint8_t* sub_p = rect->pict.data[0];
diff --combined src/lib/util.cc
index 0e250bb0885bfecde170a8fa5dd97a3690c906fe,7f370b89647557199657f47b4d47037ec5b552c0..872985024ac9a5779bfab4f9fa6fdd4988984047
@@@ -26,7 -26,6 +26,7 @@@
  #include <iomanip>
  #include <iostream>
  #include <fstream>
 +#include <climits>
  #ifdef DVDOMATIC_POSIX
  #include <execinfo.h>
  #include <cxxabi.h>
@@@ -59,11 -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;
  
@@@ -247,7 -244,7 +247,7 @@@ dvdomatic_setup (
   *  @return FFmpeg crop filter string.
   */
  string
- crop_string (Position start, Size size)
+ crop_string (Position start, libdcp::Size size)
  {
        stringstream s;
        s << "crop=" << size.width << ":" << size.height << ":" << start.x << ":" << start.y;
@@@ -333,100 -330,25 +333,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
  {
 -      DCPFrameRate dfr;
 +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)
 +{
 +      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;
  
 -      dfr.run_fast = (fps != rint (fps));
 -      dfr.frames_per_second = rint (fps);
 -      dfr.skip = 1;
 +      /* Start with the ones without skip / repeat so they will get matched in preference to skipped/repeated ones */
 +      for (list<int>::const_iterator i = allowed_dcp_frame_rates.begin(); i != allowed_dcp_frame_rates.end(); ++i) {
 +              candidates.push_back (FrameRateCandidate (*i, *i));
 +      }
 +
 +      /* Then the skip/repeat ones */
 +      for (list<int>::const_iterator i = allowed_dcp_frame_rates.begin(); i != allowed_dcp_frame_rates.end(); ++i) {
 +              candidates.push_back (FrameRateCandidate (float (*i) / 2, *i));
 +              candidates.push_back (FrameRateCandidate (float (*i) * 2, *i));
 +      }
 +
 +      /* Pick the best one, bailing early if we hit an exact match */
 +      float error = numeric_limits<float>::max ();
 +      boost::optional<FrameRateCandidate> best;
 +      list<FrameRateCandidate>::iterator i = candidates.begin();
 +      while (i != candidates.end()) {
 +              
 +              if (about_equal (i->source, source_fps)) {
 +                      best = *i;
 +                      break;
 +              }
 +
 +              float const e = fabs (i->source - source_fps);
 +              if (e < error) {
 +                      error = e;
 +                      best = *i;
 +              }
 +
 +              ++i;
 +      }
  
 -      /* XXX: somewhat arbitrary */
 -      if (fps == 50) {
 -              dfr.frames_per_second = 25;
 -              dfr.skip = 2;
 +      if (!best) {
 +              throw EncodeError ("cannot find a suitable DCP frame rate for this source");
        }
  
 -      return dfr;
 +      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.
@@@ -455,7 -377,6 +455,6 @@@ dcp_audio_channels (int f
        return f;
  }
  
  bool operator== (Crop const & a, Crop const & b)
  {
        return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom);
diff --combined src/wx/film_editor.cc
index bbaeb17c467d22d20010bd140dbb630406eb7326,72f2d48071316beeec6c7c2a0fc27d7974763642..4e99bcd960da73a6ce9fe99b40c065ab7c98baf4
@@@ -140,26 -140,15 +140,15 @@@ FilmEditor::make_film_panel (
                video_control (add_label_to_sizer (_film_sizer, _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));
 +              _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"));
 -              _dcp_trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
 -              s->Add (video_control (_dcp_trim_end));
 +              _trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
 +              s->Add (video_control (_trim_end));
  
                _film_sizer->Add (s);
        }
  
-       _multiple_reels = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Make multiple reels"));
-       _film_sizer->Add (_multiple_reels);
-       {
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-               _reel_size = new wxSpinCtrl (_film_panel, wxID_ANY);
-               s->Add (_reel_size);
-               add_label_to_sizer (s, _film_panel, "Gb each");
-               _film_sizer->Add (s);
-       }
        _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, wxT ("A/B"));
        video_control (_dcp_ab);
        _film_sizer->Add (_dcp_ab, 1);
        for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) {
                _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ()));
        }
-       _reel_size->SetRange(1, 1000);
  }
  
  void
@@@ -202,10 -189,8 +189,8 @@@ FilmEditor::connect_to_widgets (
        _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_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);
-       _multiple_reels->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::multiple_reels_toggled), 0, this);
-       _reel_size->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::reel_size_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);
@@@ -304,8 -289,8 +289,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);
  }
  
@@@ -478,32 -463,6 +463,6 @@@ FilmEditor::trust_content_header_change
        _film->set_trust_content_header (_trust_content_header->GetValue ());
  }
  
- void
- FilmEditor::multiple_reels_toggled (wxCommandEvent &)
- {
-       if (!_film) {
-               return;
-       }
-       if (_multiple_reels->GetValue()) {
-               _film->set_reel_size (_reel_size->GetValue() * 1e9);
-       } else {
-               _film->unset_reel_size ();
-       }
-       setup_reel_control_sensitivity ();
- }
- void
- FilmEditor::reel_size_changed (wxCommandEvent &)
- {
-       if (!_film) {
-               return;
-       }
-       _film->set_reel_size (static_cast<uint64_t> (_reel_size->GetValue()) * 1e9);
- }
  /** Called when the DCP A/B switch has been toggled */
  void
  FilmEditor::dcp_ab_toggled (wxCommandEvent &)
@@@ -661,12 -620,10 +620,12 @@@ FilmEditor::film_changed (Film::Propert
                } 
                _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 ()));
        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::REEL_SIZE:
-               if (_film->reel_size()) {
-                       checked_set (_multiple_reels, true);
-                       checked_set (_reel_size, _film->reel_size().get() / 1e9);
-               } else {
-                       checked_set (_multiple_reels, false);
-               }
-               setup_reel_control_sensitivity ();
-               break;
        case Film::AUDIO_GAIN:
                checked_set (_audio_gain, _film->audio_gain ());
                break;
@@@ -813,9 -761,8 +763,8 @@@ FilmEditor::set_film (shared_ptr<Film> 
        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::REEL_SIZE);
        film_changed (Film::DCP_AB);
        film_changed (Film::CONTENT_AUDIO_STREAM);
        film_changed (Film::EXTERNAL_AUDIO);
@@@ -858,10 -805,8 +807,8 @@@ FilmEditor::set_things_sensitive (bool 
        _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);
-       _multiple_reels->Enable (s);
-       _reel_size->Enable (s);
        _dcp_ab->Enable (s);
        _colour_lut->Enable (s);
        _j2k_bandwidth->Enable (s);
  
        setup_subtitle_control_sensitivity ();
        setup_audio_control_sensitivity ();
-       setup_reel_control_sensitivity ();
  }
  
  /** Called when the `Edit filters' button has been clicked */
@@@ -976,23 -920,23 +922,23 @@@ FilmEditor::still_duration_changed (wxC
  }
  
  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
@@@ -1213,9 -1157,3 +1159,3 @@@ FilmEditor::external_audio_changed (wxC
  
        _film->set_external_audio (a);
  }
- void
- FilmEditor::setup_reel_control_sensitivity ()
- {
-       _reel_size->Enable (_multiple_reels->GetValue ());
- }
diff --combined src/wx/film_editor.h
index 323aead95fb3affef35e62385b66e7facd28a09a,8a900f1e4bedf05bf325183739d96e0b34383ec5..7272315fcfedc88af94bf8bc9b9f3f977057652e
@@@ -63,10 -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 multiple_reels_toggled (wxCommandEvent &);
-       void reel_size_changed (wxCommandEvent &);
        void dcp_content_type_changed (wxCommandEvent &);
        void dcp_ab_toggled (wxCommandEvent &);
        void scaler_changed (wxCommandEvent &);
@@@ -94,7 -92,6 +92,6 @@@
        void setup_formats ();
        void setup_subtitle_control_sensitivity ();
        void setup_audio_control_sensitivity ();
-       void setup_reel_control_sensitivity ();
        void setup_streams ();
        void setup_audio_details ();
        
        /** The Film's duration for still sources */
        wxSpinCtrl* _still_duration;
  
 -      wxSpinCtrl* _dcp_trim_start;
 -      wxSpinCtrl* _dcp_trim_end;
 +      wxSpinCtrl* _trim_start;
 +      wxSpinCtrl* _trim_end;
-       wxCheckBox* _multiple_reels;
-       wxSpinCtrl* _reel_size;
        /** Selector to generate an A/B comparison DCP */
        wxCheckBox* _dcp_ab;
  
diff --combined src/wx/film_viewer.cc
index e5da41d5e80f30c9959a43854f235d29b8d4682a,e014a8731e0a6b09b369ace8e2ff69030cd76a74..4176f4f426e8287f1dffc16b335542ed132a0e05
@@@ -44,7 -44,6 +44,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)
@@@ -99,10 -98,10 +99,10 @@@ FilmViewer::film_changed (Film::Propert
                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));
@@@ -258,13 -257,13 +258,13 @@@ FilmViewer::raw_to_display (
                return;
        }
  
-       Size old_size;
+       libdcp::Size old_size;
        if (_display_frame) {
                old_size = _display_frame->size();
        }
  
        /* Get a compacted image as we have to feed it to wxWidgets */
-       _display_frame = _raw_frame->scale_and_convert_to_rgb (Size (_out_width, _out_height), 0, _film->scaler(), false);
+       _display_frame = _raw_frame->scale_and_convert_to_rgb (libdcp::Size (_out_width, _out_height), 0, _film->scaler(), false);
  
        if (old_size != _display_frame->size()) {
                _clear_required = true;