Merge master.
authorCarl Hetherington <cth@carlh.net>
Wed, 8 Oct 2014 22:02:35 +0000 (23:02 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 8 Oct 2014 22:02:35 +0000 (23:02 +0100)
1  2 
ChangeLog
cscript
debian/changelog
src/lib/server.cc
src/lib/server_finder.cc
src/lib/util.cc
wscript

diff --combined ChangeLog
index c9ebcfb8bd86b61260bbebc129f84eb04c973073,04762860200054e2bc0a9b493c828cc73777f771..fce782d7a9f6a54b70488b0b59f29950440b9ef4
+++ b/ChangeLog
@@@ -1,32 -1,28 +1,57 @@@
 +2014-10-05  Carl Hetherington  <cth@carlh.net>
 +
 +      * Use a more sensible default position and size for
 +      .srt subs.
 +
+ 2014-10-08  c.hetherington  <cth@carlh.net>
+       * Make server finding more reliable when
+       there are more than a few servers.
 +2014-10-03  Carl Hetherington  <cth@carlh.net>
 +
 +      * Version 2.0.14 released.
 +
 +2014-10-01  Carl Hetherington  <cth@carlh.net>
 +
 +      * Version 2.0.13 released.
 +
 +2014-09-30  Carl Hetherington  <cth@carlh.net>
 +
 +      * Version 2.0.12 released.
 +
 +2014-09-30  Carl Hetherington  <cth@carlh.net>
 +
 +      * Add basic video fade in/out.
 +
 +2014-09-22  Carl Hetherington  <cth@carlh.net>
 +
 +      * Version 2.0.11 released.
 +
 +2014-09-18  Carl Hetherington  <cth@carlh.net>
 +
 +      * Version 2.0.10 released.
 +
+ 2014-10-08  Carl Hetherington  <cth@carlh.net>
+       * Version 1.74.2 released.
+ 2014-10-08  Carl Hetherington  <cth@carlh.net>
+       * Version 1.74.1 released.
+ 2014-10-05  Carl Hetherington  <cth@carlh.net>
+       * Bump ffmpeg version.
+ 2014-10-06  Carl Hetherington  <cth@carlh.net>
+       * Version 1.74.0 released.
+ 2014-10-06  Carl Hetherington  <cth@carlh.net>
+       * Version 1.73.9 released.
  2014-09-28  Carl Hetherington  <cth@carlh.net>
  
        * Version 1.73.8 released.
  
  2014-09-12  Carl Hetherington  <cth@carlh.net>
  
 +      * Version 2.0.9 released.
 +
 +2014-09-12  Carl Hetherington  <cth@carlh.net>
 +
 +      * Add "re-examine" option to content context menu (#339).
 +
 +2014-09-11  Carl Hetherington  <cth@carlh.net>
 +
 +      * Restore encoding optimisations for still-image sources.
 +
 +      * Add option to re-make signing chain with specified organisation,
 +      common names etc. (#354)
 +
        * Allow separate X and Y scale for subtitles (#337).
  
  2014-09-10  Carl Hetherington  <cth@carlh.net>
  
        * Fix hidden advanced preferences button in some locales.
  
 -2014-09-08  Carl Hetherington  <cth@carlh.net>
 +      * Version 2.0.8 released.
 +
 +2014-09-10  Carl Hetherington  <cth@carlh.net>
 +
 +      * Fix loading of 1.x films.
 +
 +      * Fix crash on audio analysis in some cases.
  
 -      * Version 1.73.4 released.
 +2014-09-09  Carl Hetherington  <cth@carlh.net>
 +
 +      * Version 2.0.7 released.
 +
 +2014-09-09  Carl Hetherington  <cth@carlh.net>
 +
 +      * Version 2.0.6 released.
 +
 +2014-09-09  Carl Hetherington  <cth@carlh.net>
 +
 +      * Fix missing OS X dependencies.
 +
 +      * Use a different directory for DCP-o-matic 2
 +      configuration (not the same as 1.x).
  
  2014-09-08  Carl Hetherington  <cth@carlh.net>
  
 -      * Fix failure to load Targa files.
 +      * Version 2.0.5 released.
  
 -2014-09-07  Carl Hetherington  <cth@carlh.net>
 +      * Fix hidden advanced preferences button in some locales.
 +
 +2014-09-08  Carl Hetherington  <cth@carlh.net>
  
 -      * Version 1.73.3 released.
 +      * Fix failure to load Targa files.
  
  2014-09-07  Carl Hetherington  <cth@carlh.net>
  
  
        * Fix a few bad fuzzy translations from the preferences dialog.
  
 -2014-09-03  Carl Hetherington  <cth@carlh.net>
 -
 -      * Version 1.73.2 released.
 -
  2014-09-03  Carl Hetherington  <cth@carlh.net>
  
        * Fix server certificate downloads on OS X (#376).
  
  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>
        * 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 cscript
index 603968a4d67daf02b924ff72fb1acef075f00780,5b9c3669bd958f5182b480916be209e05b35b8dc..795cf0a35000f85b6ec96a220d4fa7455b97c203
+++ b/cscript
@@@ -157,8 -157,7 +157,8 @@@ def make_control(debian_version, bits, 
  
  def dependencies(target):
      return (('ffmpeg-cdist', '2dffa11'),
 -            ('libdcp', '2001bef5b3a6256eedf1cada4977a3ba8a3732cd'))
 +            ('libdcp', '1.0'),
 +            ('libsub', None))
  
  def build(target, options):
      cmd = './waf configure --prefix=%s' % target.directory
@@@ -209,7 -208,7 +209,7 @@@ def package_debian(target, cpu, version
      target.set('CDIST_PKG_CONFIG_PATH', target.get('PKG_CONFIG_PATH'))
      if target.version == 'unstable':
          target.set('CDIST_EXTRA_CONFIGURE', '--debian-unstable')
-     target.command('dpkg-buildpackage')
+     target.command('dpkg-buildpackage -uc -us')
      
      debs = []
      for p in glob.glob('../*.deb'):
@@@ -231,7 -230,7 +231,7 @@@ def package_centos(target, cpu, version
          "%s/SOURCES/dcpomatic-%s.tar.bz2" % (topdir, version)
          )
  
 -    target.command('rpmbuild --define \'_topdir %s\' -bb build/platform/linux/dcpomatic.spec' % topdir)
 +    target.command('rpmbuild --define \'_topdir %s\' -bb build/platform/linux/dcpomatic2.spec' % topdir)
      rpms = []
  
      if cpu == "amd64":
diff --combined debian/changelog
index 1a0185aca2230394bc7e294962172f0cccfd759d,e0ce39fd5ad1e31d3708d976b0cf115007cf80de..67dbff2a4d0eeb010a69deb5c741570f948e1762
@@@ -1,4 -1,4 +1,4 @@@
 -dcpomatic (1.74.2-1) UNRELEASED; urgency=low
 +dcpomatic (2.0.14-1) UNRELEASED; urgency=low
  
    * New upstream release.
    * New upstream release.
    * New upstream release.
    * New upstream release.
    * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
+   * New upstream release.
  
-  -- Carl Hetherington <carl@d1stkfactory>  Fri, 03 Oct 2014 19:41:52 +0100
+  -- Carl Hetherington <carl@d1stkfactory>  Wed, 08 Oct 2014 10:37:21 +0100
  
  dcpomatic (0.87-1) UNRELEASED; urgency=low
  
diff --combined src/lib/server.cc
index d2c573c1b404c2e37f07269897511be56f327851,9428ba611d70d2a96de10ef3793b348bce778aff..a699be57739699c2d8558cb1290295be7e57c406
  #include <boost/algorithm/string.hpp>
  #include <boost/scoped_array.hpp>
  #include <libcxml/cxml.h>
 -#include <libdcp/raw_convert.h>
 +#include <dcp/raw_convert.h>
  #include "server.h"
  #include "util.h"
  #include "scaler.h"
  #include "image.h"
 -#include "dcp_video_frame.h"
 +#include "dcp_video.h"
  #include "config.h"
  #include "cross.h"
 -#include "player_video_frame.h"
 +#include "player_video.h"
 +#include "encoded_data.h"
  #include "safe_stringstream.h"
  
  #include "i18n.h"
@@@ -62,37 -61,16 +62,37 @@@ using boost::thread
  using boost::bind;
  using boost::scoped_array;
  using boost::optional;
 -using libdcp::Size;
 -using libdcp::raw_convert;
 +using dcp::Size;
 +using dcp::raw_convert;
  
  Server::Server (shared_ptr<Log> log, bool verbose)
 -      : _log (log)
 +      : _terminate (false)
 +      , _log (log)
        , _verbose (verbose)
 +      , _acceptor (_io_service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), Config::instance()->server_port_base()))
  {
  
  }
  
 +Server::~Server ()
 +{
 +      {
 +              boost::mutex::scoped_lock lm (_worker_mutex);
 +              _terminate = true;
 +              _empty_condition.notify_all ();
 +      }
 +
 +      for (vector<boost::thread*>::iterator i = _worker_threads.begin(); i != _worker_threads.end(); ++i) {
 +              (*i)->join ();
 +              delete *i;
 +      }
 +
 +      _io_service.stop ();
 +
 +      _broadcast.io_service.stop ();
 +      _broadcast.thread->join ();
 +}
 +
  /** @param after_read Filled in with gettimeofday() after reading the input from the network.
   *  @param after_encode Filled in with gettimeofday() after encoding the image.
   */
@@@ -112,9 -90,9 +112,9 @@@ Server::process (shared_ptr<Socket> soc
                return -1;
        }
  
 -      shared_ptr<PlayerVideoFrame> pvf (new PlayerVideoFrame (xml, socket, _log));
 +      shared_ptr<PlayerVideo> pvf (new PlayerVideo (xml, socket, _log));
  
 -      DCPVideoFrame dcp_video_frame (pvf, xml, _log);
 +      DCPVideo dcp_video_frame (pvf, xml, _log);
  
        gettimeofday (&after_read, 0);
        
@@@ -138,14 -116,10 +138,14 @@@ Server::worker_thread (
  {
        while (true) {
                boost::mutex::scoped_lock lock (_worker_mutex);
 -              while (_queue.empty ()) {
 +              while (_queue.empty () && !_terminate) {
                        _empty_condition.wait (lock);
                }
  
 +              if (_terminate) {
 +                      return;
 +              }
 +
                shared_ptr<Socket> socket = _queue.front ();
                _queue.pop_front ();
                
@@@ -212,18 -186,39 +212,18 @@@ Server::run (int num_threads
  
        _broadcast.thread = new thread (bind (&Server::broadcast_thread, this));
        
 -      boost::asio::io_service io_service;
 -
 -      boost::asio::ip::tcp::acceptor acceptor (
 -              io_service,
 -              boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), Config::instance()->server_port_base ())
 -              );
 -      
 -      while (true) {
 -              shared_ptr<Socket> socket (new Socket);
 -              acceptor.accept (socket->socket ());
 -
 -              boost::mutex::scoped_lock lock (_worker_mutex);
 -              
 -              /* Wait until the queue has gone down a bit */
 -              while (int (_queue.size()) >= num_threads * 2) {
 -                      _full_condition.wait (lock);
 -              }
 -              
 -              _queue.push_back (socket);
 -              _empty_condition.notify_all ();
 -      }
 +      start_accept ();
 +      _io_service.run ();
  }
  
  void
  Server::broadcast_thread ()
  try
  {
 -      boost::asio::io_service io_service;
 -
        boost::asio::ip::address address = boost::asio::ip::address_v4::any ();
        boost::asio::ip::udp::endpoint listen_endpoint (address, Config::instance()->server_port_base() + 1);
  
 -      _broadcast.socket = new boost::asio::ip::udp::socket (io_service);
 +      _broadcast.socket = new boost::asio::ip::udp::socket (_broadcast.io_service);
        _broadcast.socket->open (listen_endpoint.protocol ());
        _broadcast.socket->bind (listen_endpoint);
  
                boost::bind (&Server::broadcast_received, this)
                );
  
 -      io_service.run ();
 +      _broadcast.io_service.run ();
  }
  catch (...)
  {
@@@ -252,6 -247,9 +252,9 @@@ Server::broadcast_received (
                root->add_child("Threads")->add_child_text (raw_convert<string> (_worker_threads.size ()));
                string xml = doc.write_to_string ("UTF-8");
  
+               if (_verbose) {
+                       cout << "Offering services to master " << _broadcast.send_endpoint.address().to_string () << "\n";
+               }
                shared_ptr<Socket> socket (new Socket);
                try {
                        socket->connect (boost::asio::ip::tcp::endpoint (_broadcast.send_endpoint.address(), Config::instance()->server_port_base() + 1));
                _broadcast.send_endpoint, boost::bind (&Server::broadcast_received, this)
                );
  }
 +
 +void
 +Server::start_accept ()
 +{
 +      if (_terminate) {
 +              return;
 +      }
 +
 +      shared_ptr<Socket> socket (new Socket);
 +      _acceptor.async_accept (socket->socket (), boost::bind (&Server::handle_accept, this, socket, boost::asio::placeholders::error));
 +}
 +
 +void
 +Server::handle_accept (shared_ptr<Socket> socket, boost::system::error_code const & error)
 +{
 +      if (error) {
 +              return;
 +      }
 +
 +      boost::mutex::scoped_lock lock (_worker_mutex);
 +      
 +      /* Wait until the queue has gone down a bit */
 +      while (_queue.size() >= _worker_threads.size() * 2 && !_terminate) {
 +              _full_condition.wait (lock);
 +      }
 +      
 +      _queue.push_back (socket);
 +      _empty_condition.notify_all ();
 +
 +      start_accept ();
 +}
 +      
diff --combined src/lib/server_finder.cc
index 14cb3af5999ac5ff7ccb434d39371addcbb18a6d,a082f3babf939d6a0e240433d97db1bf43e0feb4..6371035910f6baba9e253790f356e164362f642a
@@@ -18,7 -18,7 +18,7 @@@
  */
  
  #include <libcxml/cxml.h>
 -#include <libdcp/raw_convert.h>
 +#include <dcp/raw_convert.h>
  #include "server_finder.h"
  #include "exceptions.h"
  #include "util.h"
@@@ -32,7 -32,7 +32,7 @@@ using std::vector
  using std::cout;
  using boost::shared_ptr;
  using boost::scoped_array;
 -using libdcp::raw_convert;
 +using dcp::raw_convert;
  
  ServerFinder* ServerFinder::_instance = 0;
  
@@@ -102,24 -102,31 +102,31 @@@ voi
  ServerFinder::listen_thread ()
  try
  {
+       using namespace boost::asio::ip;
+       boost::asio::io_service io_service;
+       tcp::acceptor acceptor (io_service, tcp::endpoint (tcp::v4(), Config::instance()->server_port_base() + 1));
        while (true) {
-               shared_ptr<Socket> sock (new Socket (60));
+               tcp::socket socket (io_service);
+               acceptor.accept (socket);
  
-               try {
-                       sock->accept (Config::instance()->server_port_base() + 1);
-               } catch (std::exception& e) {
-                       continue;
-               }
+               /* XXX: these reads should have timeouts, otherwise we will stop finding servers
+                  if one dies during this conversation
+               */
+               uint32_t length = 0;
+               boost::asio::read (socket, boost::asio::buffer (&length, sizeof (uint32_t)));
+               length = ntohl (length);
  
-               uint32_t length = sock->read_uint32 ();
                scoped_array<char> buffer (new char[length]);
-               sock->read (reinterpret_cast<uint8_t*> (buffer.get()), length);
+               boost::asio::read (socket, boost::asio::buffer (reinterpret_cast<uint8_t*> (buffer.get ()), length));
                
                string s (buffer.get());
                shared_ptr<cxml::Document> xml (new cxml::Document ("ServerAvailable"));
                xml->read_string (s);
-               string const ip = sock->socket().remote_endpoint().address().to_string ();
+               
+               string const ip = socket.remote_endpoint().address().to_string ();
                if (!server_found (ip)) {
                        ServerDescription sd (ip, xml->number_child<int> ("Threads"));
                        _servers.push_back (sd);
diff --combined src/lib/util.cc
index 344a9f97d00d49f539adfcda688799f34322335e,290dd20ef1f7d186f7844caa9222c175058a14e4..e0db5de2ef2aa71558d2bc8a5b3d701e50ee254d
  #endif
  #include <glib.h>
  #include <openjpeg.h>
 +#include <pangomm/init.h>
  #include <magick/MagickCore.h>
  #include <magick/version.h>
 -#include <libdcp/version.h>
 -#include <libdcp/util.h>
 -#include <libdcp/signer_chain.h>
 -#include <libdcp/signer.h>
 -#include <libdcp/raw_convert.h>
 +#include <dcp/version.h>
 +#include <dcp/util.h>
 +#include <dcp/signer.h>
 +#include <dcp/raw_convert.h>
  extern "C" {
  #include <libavcodec/avcodec.h>
  #include <libavformat/avformat.h>
  #include "scaler.h"
  #include "dcp_content_type.h"
  #include "filter.h"
 -#include "sound_processor.h"
 +#include "cinema_sound_processor.h"
  #include "config.h"
  #include "ratio.h"
  #include "job.h"
  #include "cross.h"
  #include "video_content.h"
 +#include "rect.h"
  #include "md5_digester.h"
 +#include "audio_processor.h"
  #include "safe_stringstream.h"
  #ifdef DCPOMATIC_WINDOWS
  #include "stack.hpp"
@@@ -101,8 -99,8 +101,8 @@@ using std::set_terminate
  using boost::shared_ptr;
  using boost::thread;
  using boost::optional;
 -using libdcp::Size;
 -using libdcp::raw_convert;
 +using dcp::Size;
 +using dcp::raw_convert;
  
  static boost::thread::id ui_thread;
  static boost::filesystem::path backtrace_file;
@@@ -267,6 -265,24 +267,6 @@@ ffmpeg_version_to_string (int v
        return s.str ();
  }
  
 -/** Return a user-readable string summarising the versions of our dependencies */
 -string
 -dependency_version_summary ()
 -{
 -      SafeStringStream s;
 -      s << N_("libopenjpeg ") << opj_version () << N_(", ")
 -        << N_("libavcodec ") << ffmpeg_version_to_string (avcodec_version()) << N_(", ")
 -        << N_("libavfilter ") << ffmpeg_version_to_string (avfilter_version()) << N_(", ")
 -        << N_("libavformat ") << ffmpeg_version_to_string (avformat_version()) << N_(", ")
 -        << N_("libavutil ") << ffmpeg_version_to_string (avutil_version()) << N_(", ")
 -        << N_("libswscale ") << ffmpeg_version_to_string (swscale_version()) << N_(", ")
 -        << MagickVersion << N_(", ")
 -        << N_("libssh ") << ssh_version (0) << N_(", ")
 -        << N_("libdcp ") << libdcp::version << N_(" git ") << libdcp::git_commit;
 -
 -      return s.str ();
 -}
 -
  double
  seconds (struct timeval t)
  {
@@@ -355,16 -371,14 +355,16 @@@ dcpomatic_setup (
  
        set_terminate (terminate);
  
 -      libdcp::init ();
 +      Pango::init ();
 +      dcp::init ();
        
        Ratio::setup_ratios ();
        VideoContentScale::setup_scales ();
        DCPContentType::setup_dcp_content_types ();
        Scaler::setup_scalers ();
        Filter::setup_filters ();
 -      SoundProcessor::setup_sound_processors ();
 +      CinemaSoundProcessor::setup_cinema_sound_processors ();
 +      AudioProcessor::setup_audio_processors ();
  
        ui_thread = boost::this_thread::get_id ();
  }
@@@ -387,7 -401,7 +387,7 @@@ mo_path (
  boost::filesystem::path
  mo_path ()
  {
 -      return "DCP-o-matic.app/Contents/Resources";
 +      return "DCP-o-matic 2.app/Contents/Resources";
  }
  #endif
  
@@@ -472,10 -486,7 +472,10 @@@ md5_digest (vector<boost::filesystem::p
  
                while (remaining > 0) {
                        int const t = min (remaining, buffer_size);
 -                      fread (buffer, 1, t, f);
 +                      int const r = fread (buffer, 1, t, f);
 +                      if (r != t) {
 +                              throw ReadFileError (files[i], errno);
 +                      }
                        digester.add (buffer, t);
                        remaining -= t;
  
@@@ -565,7 -576,7 +565,7 @@@ Socket::accept (int port
        _acceptor->async_accept (_socket, boost::lambda::var(ec) = boost::lambda::_1);
        do {
                _io_service.run_one ();
-       } while (ec == boost::asio::error::would_block );
+       } while (ec == boost::asio::error::would_block);
  
        delete _acceptor;
        _acceptor = 0;
@@@ -645,17 -656,6 +645,17 @@@ stride_round_up (int c, int const * str
        return a - (a % t);
  }
  
 +/** @param n A number.
 + *  @param r Rounding `boundary' (must be a power of 2)
 + *  @return n rounded to the nearest r
 + */
 +int
 +round_to (float n, int r)
 +{
 +      assert (r == 1 || r == 2 || r == 4);
 +      return int (n + float(r) / 2) &~ (r - 1);
 +}
 +
  /** Read a sequence of key / value pairs from a text stream;
   *  the keys are the first words on the line, and the values are
   *  the remainder of the line following the key.  Lines beginning
@@@ -760,6 -760,17 +760,6 @@@ ensure_ui_thread (
        assert (boost::this_thread::get_id() == ui_thread);
  }
  
 -/** @param v Content video frame.
 - *  @param audio_sample_rate Source audio sample rate.
 - *  @param frames_per_second Number of video frames per second.
 - *  @return Equivalent number of audio frames for `v'.
 - */
 -int64_t
 -video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second)
 -{
 -      return ((int64_t) v * audio_sample_rate / frames_per_second);
 -}
 -
  string
  audio_channel_name (int c)
  {
@@@ -810,6 -821,59 +810,6 @@@ tidy_for_filename (string f
        return t;
  }
  
 -shared_ptr<const libdcp::Signer>
 -make_signer ()
 -{
 -      boost::filesystem::path const sd = Config::instance()->signer_chain_directory ();
 -
 -      /* Remake the chain if any of it is missing */
 -      
 -      list<boost::filesystem::path> files;
 -      files.push_back ("ca.self-signed.pem");
 -      files.push_back ("intermediate.signed.pem");
 -      files.push_back ("leaf.signed.pem");
 -      files.push_back ("leaf.key");
 -
 -      list<boost::filesystem::path>::const_iterator i = files.begin();
 -      while (i != files.end()) {
 -              boost::filesystem::path p (sd);
 -              p /= *i;
 -              if (!boost::filesystem::exists (p)) {
 -                      boost::filesystem::remove_all (sd);
 -                      boost::filesystem::create_directories (sd);
 -                      libdcp::make_signer_chain (sd, openssl_path ());
 -                      break;
 -              }
 -
 -              ++i;
 -      }
 -      
 -      libdcp::CertificateChain chain;
 -
 -      {
 -              boost::filesystem::path p (sd);
 -              p /= "ca.self-signed.pem";
 -              chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
 -      }
 -
 -      {
 -              boost::filesystem::path p (sd);
 -              p /= "intermediate.signed.pem";
 -              chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
 -      }
 -
 -      {
 -              boost::filesystem::path p (sd);
 -              p /= "leaf.signed.pem";
 -              chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
 -      }
 -
 -      boost::filesystem::path signer_key (sd);
 -      signer_key /= "leaf.key";
 -
 -      return shared_ptr<const libdcp::Signer> (new libdcp::Signer (chain, signer_key));
 -}
 -
  map<string, string>
  split_get_request (string url)
  {
        return r;
  }
  
 -libdcp::Size
 -fit_ratio_within (float ratio, libdcp::Size full_frame)
 +dcp::Size
 +fit_ratio_within (float ratio, dcp::Size full_frame, int round)
  {
        if (ratio < full_frame.ratio ()) {
 -              return libdcp::Size (rint (full_frame.height * ratio), full_frame.height);
 +              return dcp::Size (round_to (full_frame.height * ratio, round), full_frame.height);
        }
        
 -      return libdcp::Size (full_frame.width, rint (full_frame.width / ratio));
 +      return dcp::Size (full_frame.width, round_to (full_frame.width / ratio, round));
  }
  
  void *
@@@ -894,34 -958,12 +894,34 @@@ divide_with_round (int64_t a, int64_t b
        }
  }
  
 +/** Return a user-readable string summarising the versions of our dependencies */
 +string
 +dependency_version_summary ()
 +{
 +      SafeStringStream s;
 +      s << N_("libopenjpeg ") << opj_version () << N_(", ")
 +        << N_("libavcodec ") << ffmpeg_version_to_string (avcodec_version()) << N_(", ")
 +        << N_("libavfilter ") << ffmpeg_version_to_string (avfilter_version()) << N_(", ")
 +        << N_("libavformat ") << ffmpeg_version_to_string (avformat_version()) << N_(", ")
 +        << N_("libavutil ") << ffmpeg_version_to_string (avutil_version()) << N_(", ")
 +        << N_("libswscale ") << ffmpeg_version_to_string (swscale_version()) << N_(", ")
 +        << MagickVersion << N_(", ")
 +        << N_("libssh ") << ssh_version (0) << N_(", ")
 +        << N_("libdcp ") << dcp::version << N_(" git ") << dcp::git_commit;
 +
 +      return s.str ();
 +}
 +
 +/** Construct a ScopedTemporary.  A temporary filename is decided but the file is not opened
 + *  until ::open() is called.
 + */
  ScopedTemporary::ScopedTemporary ()
        : _open (0)
  {
        _file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path ();
  }
  
 +/** Close and delete the temporary file */
  ScopedTemporary::~ScopedTemporary ()
  {
        close ();       
        boost::filesystem::remove (_file, ec);
  }
  
 +/** @return temporary filename */
  char const *
  ScopedTemporary::c_str () const
  {
        return _file.string().c_str ();
  }
  
 +/** Open the temporary file.
 + *  @return File's FILE pointer.
 + */
  FILE*
  ScopedTemporary::open (char const * params)
  {
        return _open;
  }
  
 +/** Close the file */
  void
  ScopedTemporary::close ()
  {
                _open = 0;
        }
  }
 +
 +ContentTimePeriod
 +subtitle_period (AVSubtitle const & sub)
 +{
 +      ContentTime const packet_time = ContentTime::from_seconds (static_cast<double> (sub.pts) / AV_TIME_BASE);
 +
 +      ContentTimePeriod period (
 +              packet_time + ContentTime::from_seconds (sub.start_display_time / 1e3),
 +              packet_time + ContentTime::from_seconds (sub.end_display_time / 1e3)
 +              );
 +
 +      return period;
 +}
diff --combined wscript
index 58df6ab77d5e81d877410199fc515b7936edf829,09066583a40c71def4a0fe7d13b9b7e845d2c585..fccf5324532a58a945a964e933d9a6d9be407444
+++ b/wscript
@@@ -3,7 -3,7 +3,7 @@@ import o
  import sys
  
  APPNAME = 'dcpomatic'
 -VERSION = '1.74.2devel'
 +VERSION = '2.0.14devel'
  
  def options(opt):
      opt.load('compiler_cxx')
@@@ -57,15 -57,10 +57,15 @@@ def dynamic_openjpeg(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.2', mandatory=True)
  
 +def static_sub(conf):
 +    conf.check_cfg(package='libsub', atleast_version='0.01.0', args='--cflags', uselib_store='SUB', mandatory=True)
 +    conf.env.DEFINES_SUB = [f.replace('\\', '') for f in conf.env.DEFINES_SUB]
 +    conf.env.STLIB_SUB = ['sub']
 +
  def static_dcp(conf, static_boost, static_xmlpp, static_xmlsec, static_ssh):
 -    conf.check_cfg(package='libdcp', atleast_version='0.96', args='--cflags', uselib_store='DCP', mandatory=True)
 +    conf.check_cfg(package='libdcp-1.0', atleast_version='0.96', args='--cflags', uselib_store='DCP', mandatory=True)
      conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP]
 -    conf.env.STLIB_DCP = ['dcp', 'asdcp-libdcp', 'kumu-libdcp']
 +    conf.env.STLIB_DCP = ['dcp-1.0', 'asdcp-libdcp-1.0', 'kumu-libdcp-1.0']
      conf.env.LIB_DCP = ['glibmm-2.4', 'ssl', 'crypto', 'bz2', 'xslt']
  
      if static_boost:
          conf.env.LIB_DCP.append('ssh')
  
  def dynamic_dcp(conf):
 -    conf.check_cfg(package='libdcp', atleast_version='0.97.0', args='--cflags --libs', uselib_store='DCP', mandatory=True)
 +    conf.check_cfg(package='libdcp-1.0', atleast_version='0.92', args='--cflags --libs', uselib_store='DCP', mandatory=True)
      conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP]
  
 +def dynamic_sub(conf):
 +    conf.check_cfg(package='libsub', atleast_version='0.01.0', args='--cflags --libs', uselib_store='SUB', mandatory=True)
 +    conf.env.DEFINES_SUB = [f.replace('\\', '') for f in conf.env.DEFINES_SUB]
 +
  def dynamic_ssh(conf):
      conf.check_cc(fragment="""
                             #include <libssh/libssh.h>\n
@@@ -191,7 -182,7 +191,7 @@@ def configure(conf)
  
      # Common CXXFLAGS
      conf.env.append_value('CXXFLAGS', ['-D__STDC_CONSTANT_MACROS', '-D__STDC_LIMIT_MACROS', '-msse', '-ffast-math', '-fno-strict-aliasing',
-                                        '-Wall', '-Wno-attributes', '-Wextra', '-D_FILE_OFFSET_BITS=64'])
+                                        '-Wall', '-Wno-attributes', '-Wextra', '-Wno-unused-result', '-D_FILE_OFFSET_BITS=64'])
  
      if conf.options.enable_debug:
          conf.env.append_value('CXXFLAGS', ['-g', '-DDCPOMATIC_DEBUG'])
      if conf.env.TARGET_LINUX or conf.env.TARGET_OSX:
          conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_POSIX')
          conf.env.append_value('CXXFLAGS', '-DPOSIX_LOCALE_PREFIX="%s/share/locale"' % conf.env['INSTALL_PREFIX'])
 -        conf.env.append_value('CXXFLAGS', '-DPOSIX_ICON_PREFIX="%s/share/dcpomatic"' % conf.env['INSTALL_PREFIX'])
 +        conf.env.append_value('CXXFLAGS', '-DPOSIX_ICON_PREFIX="%s/share/dcpomatic2"' % conf.env['INSTALL_PREFIX'])
          boost_lib_suffix = ''
          boost_thread = 'boost_thread'
          conf.env.append_value('LINKFLAGS', '-pthread')
      if conf.env.TARGET_DEBIAN:
          # libxml2 seems to be linked against this on Ubuntu but it doesn't mention it in its .pc file
          conf.check_cfg(package='liblzma', args='--cflags --libs', uselib_store='LZMA', mandatory=True)
 +
 +    if conf.env.TARGET_CENTOS_6 or conf.env.TARGET_CENTOS_7:
 +        # libavcodec seems to be linked against this on Centos
 +        conf.check_cfg(package='liblzma', args='--cflags --libs', uselib_store='LZMA', mandatory=True)
          
      if not conf.env.DISABLE_GUI and conf.env.TARGET_LINUX:
          conf.check_cfg(package='gtk+-2.0', args='--cflags --libs', uselib_store='GTK', mandatory=True)
          conf.env.STLIB_QUICKMAIL = ['quickmail']
          static_ffmpeg(conf)
          static_openjpeg(conf)
 +        static_sub(conf)
          static_dcp(conf, False, False, False, False)
          dynamic_boost(conf, boost_lib_suffix, boost_thread)
  
          conf.env.LIB_QUICKMAIL = ['ssh2', 'idn']
          static_ffmpeg(conf)
          static_openjpeg(conf)
 +        static_sub(conf)
          static_dcp(conf, True, True, True, True)
          static_boost(conf, boost_lib_suffix)
  
          conf.env.LIB_XMLSEC = ['ltdl']
          static_ffmpeg(conf)
          static_openjpeg(conf)
 +        static_sub(conf)
          static_dcp(conf, False, True, True, True)
          dynamic_boost(conf, boost_lib_suffix, boost_thread)
  
          dynamic_ffmpeg(conf)
          dynamic_openjpeg(conf)
          dynamic_dcp(conf)
 +        dynamic_sub(conf)
          dynamic_ssh(conf)
  
      # Not packaging; just a straight build
          dynamic_boost(conf, boost_lib_suffix, boost_thread)
          dynamic_ffmpeg(conf)
          dynamic_dcp(conf)
 +        dynamic_sub(conf)
          dynamic_openjpeg(conf)
          dynamic_ssh(conf)
  
      conf.check_cfg(package='glib-2.0', args='--cflags --libs', uselib_store='GLIB', mandatory=True)
      conf.check_cfg(package= '', path=conf.options.magickpp_config, args='--cppflags --cxxflags --libs', uselib_store='MAGICK', mandatory=True)
      conf.check_cfg(package='libzip', args='--cflags --libs', uselib_store='ZIP', mandatory=True)
 +    conf.check_cfg(package='pangomm-1.4', args='--cflags --libs', uselib_store='PANGOMM', mandatory=True)
 +    conf.check_cfg(package='cairomm-1.0', args='--cflags --libs', uselib_store='CAIROMM', mandatory=True)
  
      conf.check_cc(fragment="""
                             #include <glib.h>
@@@ -379,10 -359,10 +379,10 @@@ def build(bld)
          bld.recurse('platform/osx')
  
      for r in ['22x22', '32x32', '48x48', '64x64', '128x128']:
 -        bld.install_files('${PREFIX}/share/icons/hicolor/%s/apps' % r, 'icons/%s/dcpomatic.png' % r)
 +        bld.install_files('${PREFIX}/share/icons/hicolor/%s/apps' % r, 'icons/%s/dcpomatic2.png' % r)
  
      if not bld.env.TARGET_WINDOWS:
 -        bld.install_files('${PREFIX}/share/dcpomatic', 'icons/taskbar_icon.png')
 +        bld.install_files('${PREFIX}/share/dcpomatic2', 'icons/taskbar_icon.png')
  
      bld.add_post_fun(post)
  
@@@ -448,4 -428,4 +448,4 @@@ def pot_merge(bld)
      bld.recurse('src')
  
  def tags(bld):
 -    os.system('etags src/lib/*.cc src/lib/*.h src/wx/*.cc src/wx/*.h src/tools/*.cc src/tools/*.h')
 +    os.system('etags src/lib/*.cc src/lib/*.h src/wx/*.cc src/wx/*.h src/tools/*.cc src/tools/*.h test/*.cc')