Merge master.
authorCarl Hetherington <cth@carlh.net>
Mon, 8 Sep 2014 00:09:22 +0000 (01:09 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 8 Sep 2014 00:09:22 +0000 (01:09 +0100)
1  2 
ChangeLog
src/lib/video_content.cc
src/lib/video_content.h
src/lib/video_content_scale.cc
src/lib/video_content_scale.h
src/lib/wscript

diff --combined ChangeLog
index d8210ef9d992dc21feecc26cc60c2e63a347e9d6,7cf91220546ac74ae8824eca0b0c93daa2756d65..2e09eba80bd80c359872e8f19916bbeea10d7e93
+++ b/ChangeLog
@@@ -1,3 -1,7 +1,7 @@@
+ 2014-09-07  Carl Hetherington  <cth@carlh.net>
+       * Version 1.73.3 released.
  2014-09-07  Carl Hetherington  <cth@carlh.net>
  
        * Put no stretch / no scale in the set of choices for default
  
  2014-08-29  Carl Hetherington  <cth@carlh.net>
  
 +      * Version 2.0.4 released.
 +
 +2014-08-24  Carl Hetherington  <cth@carlh.net>
 +
 +      * Version 2.0.3 released.
 +
 +2014-08-24  Carl Hetherington  <cth@carlh.net>
 +
 +      * Version 2.0.2 released.
 +
 +2014-08-06  Carl Hetherington  <cth@carlh.net>
 +
 +      * Version 2.0.1 released.
 +
 +2014-07-15  Carl Hetherington  <cth@carlh.net>
 +
 +      * A variety of changes were made on the 2.0 branch
 +      but not documented in the ChangeLog.  Most sigificantly:
 +
 +      - DCP import
 +      - Creation of DCPs with proper XML subtitles
 +      - Import of .srt and .xml subtitles
 +      - Audio processing framework (with some basic processors).
 +
 +2014-03-07  Carl Hetherington  <cth@carlh.net>
 +
 +      * Add subtitle view.
        * Some improvements to the manual.
  
  2014-08-26  Carl Hetherington  <cth@carlh.net>
@@@ -94,7 -71,6 +98,7 @@@
        * Attempt to fix random crashes on OS X (especially during encodes)
        thought to be caused by multiple threads using (different) stringstreams
        at the same time; see src/lib/safe_stringstream.
 +>>>>>>> origin/master
  
  2014-08-09  Carl Hetherington  <cth@carlh.net>
  
  2014-07-10  Carl Hetherington  <cth@carlh.net>
  
        * Version 1.72.2 released.
 +>>>>>>> origin/master
  
  2014-07-10  Carl Hetherington  <cth@carlh.net>
  
diff --combined src/lib/video_content.cc
index 9822d77634c659a583de5314a4904032da104ec4,13f2cf51676a9d10bb3b6acce245457d1dd2db66..b5097b35546ecd95d5dc75031911f206b5ac844f
@@@ -19,8 -19,8 +19,8 @@@
  
  #include <iomanip>
  #include <libcxml/cxml.h>
 -#include <libdcp/colour_matrix.h>
 -#include <libdcp/raw_convert.h>
 +#include <dcp/colour_matrix.h>
 +#include <dcp/raw_convert.h>
  #include "video_content.h"
  #include "video_examiner.h"
  #include "compose.hpp"
  #include "film.h"
  #include "exceptions.h"
  #include "frame_rate_change.h"
 +#include "log.h"
  #include "safe_stringstream.h"
  
  #include "i18n.h"
  
 +#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
 +
  int const VideoContentProperty::VIDEO_SIZE      = 0;
  int const VideoContentProperty::VIDEO_FRAME_RATE  = 1;
  int const VideoContentProperty::VIDEO_FRAME_TYPE  = 2;
@@@ -54,13 -51,12 +54,11 @@@ using std::max
  using boost::shared_ptr;
  using boost::optional;
  using boost::dynamic_pointer_cast;
 -using libdcp::raw_convert;
 +using dcp::raw_convert;
  
- vector<VideoContentScale> VideoContentScale::_scales;
  VideoContent::VideoContent (shared_ptr<const Film> f)
        : Content (f)
        , _video_length (0)
 -      , _original_video_frame_rate (0)
        , _video_frame_rate (0)
        , _video_frame_type (VIDEO_FRAME_TYPE_2D)
        , _scale (Config::instance()->default_scale ())
        setup_default_colour_conversion ();
  }
  
 -VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
 +VideoContent::VideoContent (shared_ptr<const Film> f, DCPTime s, ContentTime len)
        : Content (f, s)
        , _video_length (len)
 -      , _original_video_frame_rate (0)
        , _video_frame_rate (0)
        , _video_frame_type (VIDEO_FRAME_TYPE_2D)
        , _scale (Config::instance()->default_scale ())
@@@ -81,6 -78,7 +79,6 @@@
  VideoContent::VideoContent (shared_ptr<const Film> f, boost::filesystem::path p)
        : Content (f, p)
        , _video_length (0)
 -      , _original_video_frame_rate (0)
        , _video_frame_rate (0)
        , _video_frame_type (VIDEO_FRAME_TYPE_2D)
        , _scale (Config::instance()->default_scale ())
        setup_default_colour_conversion ();
  }
  
 -VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
 +VideoContent::VideoContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
        : Content (f, node)
  {
 -      _video_length = node->number_child<VideoContent::Frame> ("VideoLength");
        _video_size.width = node->number_child<int> ("VideoWidth");
        _video_size.height = node->number_child<int> ("VideoHeight");
        _video_frame_rate = node->number_child<float> ("VideoFrameRate");
 -      _original_video_frame_rate = node->optional_number_child<float> ("OriginalVideoFrameRate").get_value_or (_video_frame_rate);
 +
 +      if (version < 32) {
 +              /* DCP-o-matic 1.0 branch */
 +              _video_length = ContentTime::from_frames (node->number_child<int64_t> ("VideoLength"), _video_frame_rate);
 +      } else {
 +              _video_length = ContentTime (node->number_child<ContentTime::Type> ("VideoLength"));
 +      }
 +      
        _video_frame_type = static_cast<VideoFrameType> (node->number_child<int> ("VideoFrameType"));
        _crop.left = node->number_child<int> ("LeftCrop");
        _crop.right = node->number_child<int> ("RightCrop");
@@@ -158,6 -150,7 +156,6 @@@ VideoContent::VideoContent (shared_ptr<
        }
  
        _video_size = ref->video_size ();
 -      _original_video_frame_rate = ref->original_video_frame_rate ();
        _video_frame_rate = ref->video_frame_rate ();
        _video_frame_type = ref->video_frame_type ();
        _crop = ref->crop ();
@@@ -169,10 -162,11 +167,10 @@@ voi
  VideoContent::as_xml (xmlpp::Node* node) const
  {
        boost::mutex::scoped_lock lm (_mutex);
 -      node->add_child("VideoLength")->add_child_text (raw_convert<string> (_video_length));
 +      node->add_child("VideoLength")->add_child_text (raw_convert<string> (_video_length.get ()));
        node->add_child("VideoWidth")->add_child_text (raw_convert<string> (_video_size.width));
        node->add_child("VideoHeight")->add_child_text (raw_convert<string> (_video_size.height));
        node->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
 -      node->add_child("OriginalVideoFrameRate")->add_child_text (raw_convert<string> (_original_video_frame_rate));
        node->add_child("VideoFrameType")->add_child_text (raw_convert<string> (static_cast<int> (_video_frame_type)));
        _crop.as_xml (node);
        _scale.as_xml (node->add_child("Scale"));
  void
  VideoContent::setup_default_colour_conversion ()
  {
 -      _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
 +      _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
  }
  
  void
  VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
  {
        /* These examiner calls could call other content methods which take a lock on the mutex */
 -      libdcp::Size const vs = d->video_size ();
 +      dcp::Size const vs = d->video_size ();
        float const vfr = d->video_frame_rate ();
 -      
 +      ContentTime vl = d->video_length ();
 +
        {
                boost::mutex::scoped_lock lm (_mutex);
                _video_size = vs;
                _video_frame_rate = vfr;
 -              _original_video_frame_rate = vfr;
 +              _video_length = vl;
        }
 +
 +      shared_ptr<const Film> film = _film.lock ();
 +      assert (film);
 +      LOG_GENERAL ("Video length obtained from header as %1 frames", _video_length.frames (_video_frame_rate));
        
        signal_changed (VideoContentProperty::VIDEO_SIZE);
        signal_changed (VideoContentProperty::VIDEO_FRAME_RATE);
 +      signal_changed (ContentProperty::LENGTH);
  }
  
  
@@@ -337,17 -325,14 +335,17 @@@ VideoContent::technical_summary () cons
  {
        return String::compose (
                "video: length %1, size %2x%3, rate %4",
 -              video_length_after_3d_combine(), video_size().width, video_size().height, video_frame_rate()
 +              video_length_after_3d_combine().seconds(),
 +              video_size().width,
 +              video_size().height,
 +              video_frame_rate()
                );
  }
  
 -libdcp::Size
 +dcp::Size
  VideoContent::video_size_after_3d_split () const
  {
 -      libdcp::Size const s = video_size ();
 +      dcp::Size const s = video_size ();
        switch (video_frame_type ()) {
        case VIDEO_FRAME_TYPE_2D:
        case VIDEO_FRAME_TYPE_3D_ALTERNATE:
        case VIDEO_FRAME_TYPE_3D_RIGHT:
                return s;
        case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
 -              return libdcp::Size (s.width / 2, s.height);
 +              return dcp::Size (s.width / 2, s.height);
        case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
 -              return libdcp::Size (s.width, s.height / 2);
 +              return dcp::Size (s.width, s.height / 2);
        }
  
        assert (false);
@@@ -375,21 -360,28 +373,21 @@@ VideoContent::set_colour_conversion (Co
  }
  
  /** @return Video size after 3D split and crop */
 -libdcp::Size
 +dcp::Size
  VideoContent::video_size_after_crop () const
  {
        return crop().apply (video_size_after_3d_split ());
  }
  
  /** @param t A time offset from the start of this piece of content.
 - *  @return Corresponding frame index.
 + *  @return Corresponding time with respect to the content.
   */
 -VideoContent::Frame
 -VideoContent::time_to_content_video_frames (Time t) const
 +ContentTime
 +VideoContent::dcp_time_to_content_time (DCPTime t) const
  {
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
 -      
 -      FrameRateChange frc (video_frame_rate(), film->video_frame_rate());
 -
 -      /* Here we are converting from time (in the DCP) to a frame number in the content.
 -         Hence we need to use the DCP's frame rate and the double/skip correction, not
 -         the source's rate.
 -      */
 -      return t * film->video_frame_rate() / (frc.factor() * TIME_HZ);
 +      return ContentTime (t, FrameRateChange (video_frame_rate(), film->video_frame_rate()));
  }
  
  void
@@@ -433,138 -425,3 +431,3 @@@ VideoContent::set_video_frame_rate (flo
        signal_changed (VideoContentProperty::VIDEO_FRAME_RATE);
  }
  
- VideoContentScale::VideoContentScale (Ratio const * r)
-       : _ratio (r)
-       , _scale (true)
- {
- }
- VideoContentScale::VideoContentScale ()
-       : _ratio (0)
-       , _scale (false)
- {
- }
- VideoContentScale::VideoContentScale (bool scale)
-       : _ratio (0)
-       , _scale (scale)
- {
- }
- VideoContentScale::VideoContentScale (cxml::NodePtr node)
-       : _ratio (0)
-       , _scale (true)
- {
-       optional<string> r = node->optional_string_child ("Ratio");
-       if (r) {
-               _ratio = Ratio::from_id (r.get ());
-       } else {
-               _scale = node->bool_child ("Scale");
-       }
- }
- void
- VideoContentScale::as_xml (xmlpp::Node* node) const
- {
-       if (_ratio) {
-               node->add_child("Ratio")->add_child_text (_ratio->id ());
-       } else {
-               node->add_child("Scale")->add_child_text (_scale ? "1" : "0");
-       }
- }
- string
- VideoContentScale::id () const
- {
-       SafeStringStream s;
-       
-       if (_ratio) {
-               s << _ratio->id ();
-       } else {
-               s << (_scale ? "S1" : "S0");
-       }
-       
-       return s.str ();
- }
- string
- VideoContentScale::name () const
- {
-       if (_ratio) {
-               return _ratio->nickname ();
-       }
-       if (_scale) {
-               return _("No stretch");
-       }
-       return _("No scale");
- }
- VideoContentScale
- VideoContentScale::from_id (string id)
- {
-       Ratio const * r = Ratio::from_id (id);
-       if (r) {
-               return VideoContentScale (r);
-       }
-       if (id == "S0") {
-               return VideoContentScale (false);
-       }
-       return VideoContentScale (true);
- }
-               
- /** @param display_container Size of the container that we are displaying this content in.
-  *  @param film_container The size of the film's image.
-  */
- dcp::Size
- VideoContentScale::size (shared_ptr<const VideoContent> c, dcp::Size display_container, dcp::Size film_container, int round) const
- {
-       if (_ratio) {
-               return fit_ratio_within (_ratio->ratio (), display_container, round);
-       }
-       dcp::Size const ac = c->video_size_after_crop ();
-       /* Force scale if the film_container is smaller than the content's image */
-       if (_scale || film_container.width < ac.width || film_container.height < ac.height) {
-               return fit_ratio_within (ac.ratio (), display_container, 1);
-       }
-       /* Scale the image so that it will be in the right place in film_container, even if display_container is a
-          different size.
-       */
-       return dcp::Size (
-               round_to (c->video_size().width  * float(display_container.width)  / film_container.width, round),
-               round_to (c->video_size().height * float(display_container.height) / film_container.height, round)
-               );
- }
- void
- VideoContentScale::setup_scales ()
- {
-       vector<Ratio const *> ratios = Ratio::all ();
-       for (vector<Ratio const *>::const_iterator i = ratios.begin(); i != ratios.end(); ++i) {
-               _scales.push_back (VideoContentScale (*i));
-       }
-       _scales.push_back (VideoContentScale (true));
-       _scales.push_back (VideoContentScale (false));
- }
- bool
- operator== (VideoContentScale const & a, VideoContentScale const & b)
- {
-       return (a.ratio() == b.ratio() && a.scale() == b.scale());
- }
- bool
- operator!= (VideoContentScale const & a, VideoContentScale const & b)
- {
-       return (a.ratio() != b.ratio() || a.scale() != b.scale());
- }
diff --combined src/lib/video_content.h
index d32769b5a9dde2ec41128207983e04b9721b8c0e,3a7b44306ca80890b10deca55c496127860ff352..b3c81d9c3eb807bbc3866d384141ce26f1aaff47
@@@ -22,6 -22,7 +22,7 @@@
  
  #include "content.h"
  #include "colour_conversion.h"
+ #include "video_content_scale.h"
  
  class VideoExaminer;
  class Ratio;
@@@ -37,54 -38,15 +38,15 @@@ public
        static int const COLOUR_CONVERSION;
  };
  
- class VideoContentScale
- {
- public:
-       VideoContentScale ();
-       VideoContentScale (Ratio const *);
-       VideoContentScale (bool);
-       VideoContentScale (cxml::NodePtr);
-       dcp::Size size (boost::shared_ptr<const VideoContent>, dcp::Size, dcp::Size, int round) const;
-       std::string id () const;
-       std::string name () const;
-       void as_xml (xmlpp::Node *) const;
-       Ratio const * ratio () const {
-               return _ratio;
-       }
-       bool scale () const {
-               return _scale;
-       }
-       static void setup_scales ();
-       static std::vector<VideoContentScale> all () {
-               return _scales;
-       }
-       static VideoContentScale from_id (std::string id);
- private:
-       /** a ratio to stretch the content to, or 0 for no stretch */
-       Ratio const * _ratio;
-       /** true if we want to scale the content */
-       bool _scale;
-       static std::vector<VideoContentScale> _scales;
- };
- bool operator== (VideoContentScale const & a, VideoContentScale const & b);
- bool operator!= (VideoContentScale const & a, VideoContentScale const & b);
  class VideoContent : public virtual Content
  {
  public:
        typedef int Frame;
  
        VideoContent (boost::shared_ptr<const Film>);
 -      VideoContent (boost::shared_ptr<const Film>, Time, VideoContent::Frame);
 +      VideoContent (boost::shared_ptr<const Film>, DCPTime, ContentTime);
        VideoContent (boost::shared_ptr<const Film>, boost::filesystem::path);
 -      VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
 +      VideoContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int);
        VideoContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
  
        void as_xml (xmlpp::Node *) const;
        virtual std::string information () const;
        virtual std::string identifier () const;
  
 -      VideoContent::Frame video_length () const {
 +      ContentTime video_length () const {
                boost::mutex::scoped_lock lm (_mutex);
                return _video_length;
        }
  
 -      VideoContent::Frame video_length_after_3d_combine () const {
 +      ContentTime video_length_after_3d_combine () const {
                boost::mutex::scoped_lock lm (_mutex);
                if (_video_frame_type == VIDEO_FRAME_TYPE_3D_ALTERNATE) {
 -                      return _video_length / 2;
 +                      return ContentTime (_video_length.get() / 2);
                }
                
                return _video_length;
        }
  
 -      libdcp::Size video_size () const {
 +      dcp::Size video_size () const {
                boost::mutex::scoped_lock lm (_mutex);
                return _video_size;
        }
                return _video_frame_rate;
        }
  
 -      float original_video_frame_rate () const {
 -              boost::mutex::scoped_lock lm (_mutex);
 -              return _original_video_frame_rate;
 -      }
 -      
        void set_video_frame_type (VideoFrameType);
        void set_video_frame_rate (float);
  
                return _colour_conversion;
        }
  
 -      libdcp::Size video_size_after_3d_split () const;
 -      libdcp::Size video_size_after_crop () const;
 +      dcp::Size video_size_after_3d_split () const;
 +      dcp::Size video_size_after_crop () const;
  
 -      VideoContent::Frame time_to_content_video_frames (Time) const;
 +      ContentTime dcp_time_to_content_time (DCPTime) const;
  
        void scale_and_crop_to_fit_width ();
        void scale_and_crop_to_fit_height ();
  protected:
        void take_from_video_examiner (boost::shared_ptr<VideoExaminer>);
  
 -      VideoContent::Frame _video_length;
 -      float _original_video_frame_rate;
 +      ContentTime _video_length;
        float _video_frame_rate;
  
  private:
 -      friend class ffmpeg_pts_offset_test;
 -      friend class best_dcp_frame_rate_test_single;
 -      friend class best_dcp_frame_rate_test_double;
 -      friend class audio_sampling_rate_test;
 +      friend struct ffmpeg_pts_offset_test;
 +      friend struct best_dcp_frame_rate_test_single;
 +      friend struct best_dcp_frame_rate_test_double;
 +      friend struct audio_sampling_rate_test;
  
        void setup_default_colour_conversion ();
        
 -      libdcp::Size _video_size;
 +      dcp::Size _video_size;
        VideoFrameType _video_frame_type;
        Crop _crop;
        VideoContentScale _scale;
index 0000000000000000000000000000000000000000,e603582b89e7c5faef2d7690160786e8855b03c4..418c46eecd121fbf1d4019f27ae1f752758d5105
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,171 +1,171 @@@
 -libdcp::Size
 -VideoContentScale::size (shared_ptr<const VideoContent> c, libdcp::Size display_container, libdcp::Size film_container) const
+ /*
+     Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+     (at your option) any later version.
+     This program is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+     GNU General Public License for more details.
+     You should have received a copy of the GNU General Public License
+     along with this program; if not, write to the Free Software
+     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+ #include <boost/optional.hpp>
+ #include <libxml++/libxml++.h>
+ #include <libcxml/cxml.h>
+ #include "video_content_scale.h"
+ #include "ratio.h"
+ #include "safe_stringstream.h"
+ #include "util.h"
+ #include "i18n.h"
+ using std::vector;
+ using std::string;
+ using boost::shared_ptr;
+ using boost::optional;
+ vector<VideoContentScale> VideoContentScale::_scales;
+ VideoContentScale::VideoContentScale (Ratio const * r)
+       : _ratio (r)
+       , _scale (true)
+ {
+ }
+ VideoContentScale::VideoContentScale ()
+       : _ratio (0)
+       , _scale (false)
+ {
+ }
+ VideoContentScale::VideoContentScale (bool scale)
+       : _ratio (0)
+       , _scale (scale)
+ {
+ }
+ VideoContentScale::VideoContentScale (shared_ptr<cxml::Node> node)
+       : _ratio (0)
+       , _scale (true)
+ {
+       optional<string> r = node->optional_string_child ("Ratio");
+       if (r) {
+               _ratio = Ratio::from_id (r.get ());
+       } else {
+               _scale = node->bool_child ("Scale");
+       }
+ }
+ void
+ VideoContentScale::as_xml (xmlpp::Node* node) const
+ {
+       if (_ratio) {
+               node->add_child("Ratio")->add_child_text (_ratio->id ());
+       } else {
+               node->add_child("Scale")->add_child_text (_scale ? "1" : "0");
+       }
+ }
+ string
+ VideoContentScale::id () const
+ {
+       SafeStringStream s;
+       
+       if (_ratio) {
+               s << _ratio->id ();
+       } else {
+               s << (_scale ? "S1" : "S0");
+       }
+       
+       return s.str ();
+ }
+ string
+ VideoContentScale::name () const
+ {
+       if (_ratio) {
+               return _ratio->nickname ();
+       }
+       if (_scale) {
+               return _("No stretch");
+       }
+       return _("No scale");
+ }
+ VideoContentScale
+ VideoContentScale::from_id (string id)
+ {
+       Ratio const * r = Ratio::from_id (id);
+       if (r) {
+               return VideoContentScale (r);
+       }
+       if (id == "S0") {
+               return VideoContentScale (false);
+       }
+       return VideoContentScale (true);
+ }
+               
+ /** @param display_container Size of the container that we are displaying this content in.
+  *  @param film_container The size of the film's image.
+  */
 -              return fit_ratio_within (_ratio->ratio (), display_container);
++dcp::Size
++VideoContentScale::size (shared_ptr<const VideoContent> c, dcp::Size display_container, dcp::Size film_container, int round) const
+ {
+       if (_ratio) {
 -      libdcp::Size const ac = c->video_size_after_crop ();
++              return fit_ratio_within (_ratio->ratio (), display_container, round);
+       }
 -              return fit_ratio_within (ac.ratio (), display_container);
++      dcp::Size const ac = c->video_size_after_crop ();
+       /* Force scale if the film_container is smaller than the content's image */
+       if (_scale || film_container.width < ac.width || film_container.height < ac.height) {
 -      return libdcp::Size (
++              return fit_ratio_within (ac.ratio (), display_container, round);
+       }
+       /* Scale the image so that it will be in the right place in film_container, even if display_container is a
+          different size.
+       */
++      return dcp::Size (
+               c->video_size().width  * float(display_container.width)  / film_container.width,
+               c->video_size().height * float(display_container.height) / film_container.height
+               );
+ }
+ void
+ VideoContentScale::setup_scales ()
+ {
+       vector<Ratio const *> ratios = Ratio::all ();
+       for (vector<Ratio const *>::const_iterator i = ratios.begin(); i != ratios.end(); ++i) {
+               _scales.push_back (VideoContentScale (*i));
+       }
+       _scales.push_back (VideoContentScale (true));
+       _scales.push_back (VideoContentScale (false));
+ }
+ bool
+ operator== (VideoContentScale const & a, VideoContentScale const & b)
+ {
+       return (a.ratio() == b.ratio() && a.scale() == b.scale());
+ }
+ bool
+ operator!= (VideoContentScale const & a, VideoContentScale const & b)
+ {
+       return (a.ratio() != b.ratio() || a.scale() != b.scale());
+ }
index 0000000000000000000000000000000000000000,87dd2f1fafec05b49706c71f226d58cabadc495e..6b718d5741e09380d47abaff3e42268937178471
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,77 +1,77 @@@
 -#include <libdcp/util.h>
+ /*
+     Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+     (at your option) any later version.
+     This program is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+     GNU General Public License for more details.
+     You should have received a copy of the GNU General Public License
+     along with this program; if not, write to the Free Software
+     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+ #ifndef DCPOMATIC_VIDEO_CONTENT_SCALE_H
+ #define DCPOMATIC_VIDEO_CONTENT_SCALE_H
+ #include <vector>
+ #include <boost/shared_ptr.hpp>
 -      libdcp::Size size (boost::shared_ptr<const VideoContent>, libdcp::Size, libdcp::Size) const;
++#include <dcp/util.h>
+ namespace cxml {
+       class Node;
+ }
+ namespace xmlpp {
+       class Node;
+ }
+ class Ratio;
+ class VideoContent;
+ class VideoContentScale
+ {
+ public:
+       VideoContentScale ();
+       VideoContentScale (Ratio const *);
+       VideoContentScale (bool);
+       VideoContentScale (boost::shared_ptr<cxml::Node>);
++      dcp::Size size (boost::shared_ptr<const VideoContent>, dcp::Size, dcp::Size, int round) const;
+       std::string id () const;
+       std::string name () const;
+       void as_xml (xmlpp::Node *) const;
+       Ratio const * ratio () const {
+               return _ratio;
+       }
+       bool scale () const {
+               return _scale;
+       }
+       static void setup_scales ();
+       static std::vector<VideoContentScale> all () {
+               return _scales;
+       }
+       static VideoContentScale from_id (std::string id);
+ private:
+       /** a ratio to stretch the content to, or 0 for no stretch */
+       Ratio const * _ratio;
+       /** true if we want to scale the content */
+       bool _scale;
+       static std::vector<VideoContentScale> _scales;
+ };
+ bool operator== (VideoContentScale const & a, VideoContentScale const & b);
+ bool operator!= (VideoContentScale const & a, VideoContentScale const & b);
+ #endif
diff --combined src/lib/wscript
index 2510ebe2a481b75d4d8b0c90f3916420f43350f7,6c1da1772e696276161de2d0f9d7385bd68bf50d..3f77a5cebb9707b0575a8e8c29c30878d2f5d5e5
@@@ -7,39 -7,26 +7,39 @@@ sources = ""
            audio_buffers.cc
            audio_content.cc
            audio_decoder.cc
 +          audio_filter.cc
            audio_mapping.cc
 +          audio_processor.cc
            cinema.cc
 +          cinema_sound_processor.cc
            colour_conversion.cc
            config.cc
            content.cc
            content_factory.cc
 +          content_subtitle.cc
            cross.cc
 +          dcp_content.cc
            dcp_content_type.cc
 -          dcp_video_frame.cc
 -          decoder.cc
 +          dcp_decoder.cc
 +          dcp_examiner.cc
 +          dcp_subtitle_content.cc
 +          dcp_subtitle_decoder.cc
 +          dcp_video.cc
 +          dcpomatic_time.cc
            dolby_cp750.cc
            encoder.cc
 +          encoded_data.cc
            examine_content_job.cc
            exceptions.cc
            file_group.cc
            filter_graph.cc
            ffmpeg.cc
 +          ffmpeg_audio_stream.cc
            ffmpeg_content.cc
            ffmpeg_decoder.cc
            ffmpeg_examiner.cc
 +          ffmpeg_stream.cc
 +          ffmpeg_subtitle_stream.cc
            film.cc
            filter.cc
            frame_rate_change.cc
            image_examiner.cc
            image_proxy.cc
            isdcf_metadata.cc
 +          j2k_image_proxy.cc
            job.cc
            job_manager.cc
            kdm.cc
            log.cc
 +          magick_image_proxy.cc
            md5_digester.cc
 -          piece.cc
 +          mid_side_decoder.cc
            player.cc
 -          player_video_frame.cc
 +          player_video.cc
            playlist.cc
            ratio.cc
 +          raw_image_proxy.cc
 +          render_subtitles.cc
            resampler.cc
            safe_stringstream.cc
            scp_dcp_job.cc
            send_kdm_email_job.cc
            server.cc
            server_finder.cc
 +          single_stream_audio_content.cc
            sndfile_content.cc
            sndfile_decoder.cc
 -          sound_processor.cc
 -          subtitle.cc
 +          subrip.cc
 +          subrip_content.cc
 +          subrip_decoder.cc
            subtitle_content.cc
            subtitle_decoder.cc
            timer.cc
            types.cc
            ui_signaller.cc
            update.cc
 +          upmixer_a.cc
            util.cc
            video_content.cc
+           video_content_scale.cc
            video_decoder.cc
            writer.cc
            """
@@@ -98,13 -79,13 +99,13 @@@ def build(bld)
      else:
          obj = bld(features = 'cxx cxxshlib')
  
 -    obj.name = 'libdcpomatic'
 +    obj.name = 'libdcpomatic2'
      obj.export_includes = ['..']
      obj.uselib = """
                   AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE 
                   BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2
 -                 SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XMLPP
 -                 CURL ZIP QUICKMAIL XMLSEC
 +                 SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XML++
 +                 CURL ZIP QUICKMAIL PANGOMM CAIROMM XMLSEC
                   """
  
      if bld.env.TARGET_OSX:
      if bld.env.BUILD_STATIC:
          obj.uselib += ' XMLPP'
  
 -    obj.target = 'dcpomatic'
 +    obj.target = 'dcpomatic2'
  
 -    i18n.po_to_mo(os.path.join('src', 'lib'), 'libdcpomatic', bld)
 +    i18n.po_to_mo(os.path.join('src', 'lib'), 'libdcpomatic2', bld)
  
  def pot(bld):
      i18n.pot(os.path.join('src', 'lib'), sources, 'libdcpomatic')