Merge master.
authorCarl Hetherington <cth@carlh.net>
Tue, 1 Jul 2014 12:59:50 +0000 (13:59 +0100)
committerCarl Hetherington <cth@carlh.net>
Tue, 1 Jul 2014 12:59:50 +0000 (13:59 +0100)
16 files changed:
1  2 
src/lib/cross.cc
src/lib/ffmpeg_examiner.cc
src/lib/file_group.cc
src/lib/filter_graph.cc
src/lib/resampler.cc
src/lib/server.cc
src/lib/server_finder.cc
src/lib/update.cc
src/lib/writer.cc
src/tools/dcpomatic.cc
src/tools/dcpomatic_cli.cc
src/tools/dcpomatic_create.cc
src/tools/dcpomatic_kdm.cc
src/tools/server_test.cc
src/wx/film_editor.cc
test/job_test.cc

diff --combined src/lib/cross.cc
index 53ef5723ac50232d5e05fb8664a7b8dc3eba766d,e30cf64598f57e717745cc22291022ebbb0eccac..0224e461d5fb8193fe82841ecdb34051711b2f6a
@@@ -1,5 -1,5 +1,5 @@@
  /*
 -    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
 +    Copyright (C) 2012-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
@@@ -202,7 -202,7 +202,7 @@@ run_ffprobe (boost::filesystem::path co
  
        CloseHandle (child_stderr_write);
  
-       while (1) {
+       while (true) {
                char buffer[512];
                DWORD read;
                if (!ReadFile(child_stderr_read, buffer, sizeof(buffer), &read, 0) || read == 0) {
@@@ -247,7 -247,7 +247,7 @@@ mount_info (
                return m;
        }
        
-       while (1) {
+       while (true) {
                struct mntent* mnt = getmntent (f);
                if (!mnt) {
                        break;
index d9bcedfc528f196ba9a85b064d1f3900f32fdbbc,bc82a9700f8c3b254ea287c43483919299a37de7..949062f5bd5c3a9e5b1eace869ffc4474007560f
@@@ -1,5 -1,5 +1,5 @@@
  /*
 -    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
 +    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
@@@ -23,9 -23,6 +23,9 @@@ extern "C" 
  }
  #include "ffmpeg_examiner.h"
  #include "ffmpeg_content.h"
 +#include "ffmpeg_audio_stream.h"
 +#include "ffmpeg_subtitle_stream.h"
 +#include "util.h"
  
  #include "i18n.h"
  
@@@ -64,93 -61,55 +64,93 @@@ FFmpegExaminer::FFmpegExaminer (shared_
                }
        }
  
 -      /* Run through until we find the first audio (for each stream) and video */
 -
 +      /* Run through until we find:
 +       *   - the first video.
 +       *   - the first audio for each stream.
 +       *   - the subtitle periods for each stream.
 +       *
 +       * We have to note subtitle periods as otherwise we have no way of knowing
 +       * where we should look for subtitles (video and audio are always present,
 +       * so they are ok).
 +       */
-       while (1) {
+       while (true) {
                int r = av_read_frame (_format_context, &_packet);
                if (r < 0) {
                        break;
                }
  
 -              int frame_finished;
 -
                AVCodecContext* context = _format_context->streams[_packet.stream_index]->codec;
  
 -              if (_packet.stream_index == _video_stream && !_first_video) {
 -                      if (avcodec_decode_video2 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
 -                              _first_video = frame_time (_format_context->streams[_video_stream]);
 -                      }
 -              } else {
 -                      for (size_t i = 0; i < _audio_streams.size(); ++i) {
 -                              if (_audio_streams[i]->uses_index (_format_context, _packet.stream_index) && !_audio_streams[i]->first_audio) {
 -                                      if (avcodec_decode_audio4 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
 -                                              _audio_streams[i]->first_audio = frame_time (_audio_streams[i]->stream (_format_context));
 -                                      }
 -                              }
 +              if (_packet.stream_index == _video_stream) {
 +                      video_packet (context);
 +              }
 +              
 +              for (size_t i = 0; i < _audio_streams.size(); ++i) {
 +                      if (_audio_streams[i]->uses_index (_format_context, _packet.stream_index)) {
 +                              audio_packet (context, _audio_streams[i]);
                        }
                }
  
 -              bool have_all_audio = true;
 -              size_t i = 0;
 -              while (i < _audio_streams.size() && have_all_audio) {
 -                      have_all_audio = _audio_streams[i]->first_audio;
 -                      ++i;
 +              for (size_t i = 0; i < _subtitle_streams.size(); ++i) {
 +                      if (_subtitle_streams[i]->uses_index (_format_context, _packet.stream_index)) {
 +                              subtitle_packet (context, _subtitle_streams[i]);
 +                      }
                }
  
                av_free_packet (&_packet);
 -              
 -              if (_first_video && have_all_audio) {
 -                      break;
 +      }
 +}
 +
 +void
 +FFmpegExaminer::video_packet (AVCodecContext* context)
 +{
 +      if (_first_video) {
 +              return;
 +      }
 +
 +      int frame_finished;
 +      if (avcodec_decode_video2 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
 +              _first_video = frame_time (_format_context->streams[_video_stream]);
 +      }
 +}
 +
 +void
 +FFmpegExaminer::audio_packet (AVCodecContext* context, shared_ptr<FFmpegAudioStream> stream)
 +{
 +      if (stream->first_audio) {
 +              return;
 +      }
 +
 +      int frame_finished;
 +      if (avcodec_decode_audio4 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
 +              stream->first_audio = frame_time (stream->stream (_format_context));
 +      }
 +}
 +
 +void
 +FFmpegExaminer::subtitle_packet (AVCodecContext* context, shared_ptr<FFmpegSubtitleStream> stream)
 +{
 +      int frame_finished;
 +      AVSubtitle sub;
 +      if (avcodec_decode_subtitle2 (context, &sub, &frame_finished, &_packet) >= 0 && frame_finished) {
 +              ContentTimePeriod const period = subtitle_period (sub);
 +              if (sub.num_rects == 0 && !stream->periods.empty () && stream->periods.back().to > period.from) {
 +                      /* Finish the last subtitle */
 +                      stream->periods.back().to = period.from;
 +              } else if (sub.num_rects == 1) {
 +                      stream->periods.push_back (period);
                }
        }
  }
  
 -optional<double>
 +optional<ContentTime>
  FFmpegExaminer::frame_time (AVStream* s) const
  {
 -      optional<double> t;
 +      optional<ContentTime> t;
        
        int64_t const bet = av_frame_get_best_effort_timestamp (_frame);
        if (bet != AV_NOPTS_VALUE) {
 -              t = bet * av_q2d (s->time_base);
 +              t = ContentTime::from_seconds (bet * av_q2d (s->time_base));
        }
  
        return t;
  float
  FFmpegExaminer::video_frame_rate () const
  {
 -      AVStream* s = _format_context->streams[_video_stream];
 -
 -      if (s->avg_frame_rate.num && s->avg_frame_rate.den) {
 -              return av_q2d (s->avg_frame_rate);
 -      }
 -
 -      return av_q2d (s->r_frame_rate);
 +      /* This use of r_frame_rate is debateable; there's a few different
 +       * frame rates in the format context, but this one seems to be the most
 +       * reliable.
 +       */
 +      return av_q2d (av_stream_get_r_frame_rate (_format_context->streams[_video_stream]));
  }
  
 -libdcp::Size
 +dcp::Size
  FFmpegExaminer::video_size () const
  {
 -      return libdcp::Size (video_codec_context()->width, video_codec_context()->height);
 +      return dcp::Size (video_codec_context()->width, video_codec_context()->height);
  }
  
 -/** @return Length (in video frames) according to our content's header */
 -VideoContent::Frame
 +/** @return Length according to our content's header */
 +ContentTime
  FFmpegExaminer::video_length () const
  {
 -      VideoContent::Frame const length = (double (_format_context->duration - _format_context->start_time) / AV_TIME_BASE) * video_frame_rate();
 -      return max (1, length);
 +      ContentTime const length = ContentTime::from_seconds (double (_format_context->duration - _format_context->start_time) / AV_TIME_BASE);
 +      return ContentTime (max (int64_t (1), length.get ()));
  }
  
  string
diff --combined src/lib/file_group.cc
index 54ec8280c0a6830da2a671137dc7a4f45336b61b,048f6923316e2cae13edfa9033daaa982f143db3..9c8d43204b1b830726c073de894e7b9f5c6e9e22
@@@ -1,5 -1,5 +1,5 @@@
  /*
 -    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
 +    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
  
  */
  
 +/** @file  src/lib/file_group.cc
 + *  @brief FileGroup class.
 + */
 +
  #include <cstdio>
  #include <sndfile.h>
  #include "file_group.h"
@@@ -30,7 -26,6 +30,7 @@@
  using std::vector;
  using std::cout;
  
 +/** Construct a FileGroup with no files */
  FileGroup::FileGroup ()
        : _current_path (0)
        , _current_file (0)
  
  }
  
 +/** Construct a FileGroup with a single file */
  FileGroup::FileGroup (boost::filesystem::path p)
        : _current_path (0)
        , _current_file (0)
  {
        _paths.push_back (p);
 +      ensure_open_path (0);
        seek (0, SEEK_SET);
  }
  
 +/** Construct a FileGroup with multiple files */
  FileGroup::FileGroup (vector<boost::filesystem::path> const & p)
        : _paths (p)
        , _current_path (0)
@@@ -58,7 -50,6 +58,7 @@@
        seek (0, SEEK_SET);
  }
  
 +/** Destroy a FileGroup, closing any open file */
  FileGroup::~FileGroup ()
  {
        if (_current_file) {
@@@ -151,7 -142,7 +151,7 @@@ in
  FileGroup::read (uint8_t* buffer, int amount) const
  {
        int read = 0;
-       while (1) {
+       while (true) {
                int const this_time = fread (buffer + read, 1, amount - read, _current_file);
                read += this_time;
                if (read == amount) {
        return read;
  }
  
 +/** @return Combined length of all the files */
  int64_t
  FileGroup::length () const
  {
diff --combined src/lib/filter_graph.cc
index 5add16d19bcedac612b55eecf5bc85a7feedf99a,c11ee633185aabac54837744b99604fa1f3b359a..8b259a12d01e26c1b126c9d1296ac2632c3ee10d
@@@ -45,14 -45,14 +45,14 @@@ using std::make_pair
  using std::cout;
  using boost::shared_ptr;
  using boost::weak_ptr;
 -using libdcp::Size;
 +using dcp::Size;
  
  /** Construct a FilterGraph for the settings in a piece of content.
   *  @param content Content.
   *  @param s Size of the images to process.
   *  @param p Pixel format of the images to process.
   */
 -FilterGraph::FilterGraph (shared_ptr<const FFmpegContent> content, libdcp::Size s, AVPixelFormat p)
 +FilterGraph::FilterGraph (shared_ptr<const FFmpegContent> content, dcp::Size s, AVPixelFormat p)
        : _buffer_src_context (0)
        , _buffer_sink_context (0)
        , _size (s)
                throw DecodeError (N_("could not configure filter graph."));
        }
  
 -      /* XXX: leaking `inputs' / `outputs' ? */
 +      avfilter_inout_free (&inputs);
 +      avfilter_inout_free (&outputs);
  }
  
  FilterGraph::~FilterGraph ()
@@@ -143,7 -142,7 +143,7 @@@ FilterGraph::process (AVFrame* frame
                throw DecodeError (N_("could not push buffer into filter chain."));
        }
  
-       while (1) {
+       while (true) {
                if (av_buffersink_get_frame (_buffer_sink_context, _frame) < 0) {
                        break;
                }
   *  @return true if this chain can process images with `s' and `p', otherwise false.
   */
  bool
 -FilterGraph::can_process (libdcp::Size s, AVPixelFormat p) const
 +FilterGraph::can_process (dcp::Size s, AVPixelFormat p) const
  {
        return (_size == s && _pixel_format == p);
  }
diff --combined src/lib/resampler.cc
index 9a81df4993e5da7132938afd25db6dbf29c6d392,e7f50c4b7bf7fc2174248b2317d3d50d45813da9..e414436e8b39960c6c5f78ca16188c0781bc0407
@@@ -60,9 -60,11 +60,9 @@@ Resampler::~Resampler (
        swr_free (&_swr_context);
  }
  
 -pair<shared_ptr<const AudioBuffers>, AudioContent::Frame>
 -Resampler::run (shared_ptr<const AudioBuffers> in, AudioContent::Frame frame)
 +shared_ptr<const AudioBuffers>
 +Resampler::run (shared_ptr<const AudioBuffers> in)
  {
 -      AudioContent::Frame const resamp_time = swr_next_pts (_swr_context, frame * _out_rate) / _in_rate;
 -              
        /* Compute the resampled frames count and add 32 for luck */
        int const max_resampled_frames = ceil ((double) in->frames() * _out_rate / _in_rate) + 32;
        shared_ptr<AudioBuffers> resampled (new AudioBuffers (_channels, max_resampled_frames));
@@@ -78,7 -80,7 +78,7 @@@
        }
        
        resampled->set_frames (resampled_frames);
 -      return make_pair (resampled, resamp_time);
 +      return resampled;
  }     
  
  shared_ptr<const AudioBuffers>
@@@ -89,7 -91,7 +89,7 @@@ Resampler::flush (
        int64_t const pass_size = 256;
        shared_ptr<AudioBuffers> pass (new AudioBuffers (_channels, 256));
  
-       while (1) {
+       while (true) {
                int const frames = swr_convert (_swr_context, (uint8_t **) pass->data(), pass_size, 0, 0);
                
                if (frames < 0) {
diff --combined src/lib/server.cc
index fe792e307424501b2c8a6df845c4b202f01d1504,f1c6d6c44afd0e34d6eb3436a1a493042a6a52c8..9d1925de1a4a2c80cf0167b5f484e099c6237aab
@@@ -29,7 -29,7 +29,7 @@@
  #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"
@@@ -62,37 -62,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.
   */
@@@ -136,16 -115,12 +136,16 @@@ Server::process (shared_ptr<Socket> soc
  void
  Server::worker_thread ()
  {
-       while (1) {
+       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 -187,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 (...)
  {
@@@ -268,35 -264,3 +268,35 @@@ Server::broadcast_received (
                _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 de8a3852c5cf45c0abd52faaf94b4f08ee24e94c,65e0940a0a11aab9469f32247a0a58ba36e11ba3..9b0cd037679f9351c5a9112537dbef283235a4ed
@@@ -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"
@@@ -33,7 -33,7 +33,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;
  
@@@ -103,7 -103,7 +103,7 @@@ voi
  ServerFinder::listen_thread ()
  try
  {
-       while (1) {
+       while (true) {
                shared_ptr<Socket> sock (new Socket (60));
  
                try {
diff --combined src/lib/update.cc
index c7527ee493624247468b365cde6b2363acb5496f,af3e46f0e7d14ea1abb8d64c4c4e467143588547..44ecbb232c26b03214f23d3191c54fedd9a9071a
@@@ -22,7 -22,7 +22,7 @@@
  #include <boost/algorithm/string.hpp>
  #include <curl/curl.h>
  #include <libcxml/cxml.h>
 -#include <libdcp/raw_convert.h>
 +#include <dcp/raw_convert.h>
  #include "update.h"
  #include "version.h"
  #include "ui_signaller.h"
@@@ -33,9 -33,8 +33,9 @@@ using std::cout
  using std::min;
  using std::string;
  using std::stringstream;
 -using libdcp::raw_convert;
 +using dcp::raw_convert;
  
 +/** Singleton instance */
  UpdateChecker* UpdateChecker::_instance = 0;
  
  static size_t
@@@ -44,9 -43,6 +44,9 @@@ write_callback_wrapper (void* data, siz
        return reinterpret_cast<UpdateChecker*>(user)->write_callback (data, size, nmemb);
  }
  
 +/** Construct an UpdateChecker.  This sets things up and starts a thread to
 + *  do the work.
 + */
  UpdateChecker::UpdateChecker ()
        : _buffer (new char[BUFFER_SIZE])
        , _offset (0)
@@@ -78,7 -74,6 +78,7 @@@ UpdateChecker::~UpdateChecker (
        delete[] _buffer;
  }
  
 +/** Start running the update check */
  void
  UpdateChecker::run ()
  {
@@@ -90,8 -85,7 +90,8 @@@
  void
  UpdateChecker::thread ()
  {
-       while (1) {
+       while (true) {
 +              /* Block until there is something to do */
                boost::mutex::scoped_lock lock (_process_mutex);
                while (_to_do == 0) {
                        _condition.wait (lock);
                
                try {
                        _offset = 0;
 +
 +                      /* Perform the request */
                        
                        int r = curl_easy_perform (_curl);
                        if (r != CURLE_OK) {
                                set_state (FAILED);
                                return;
                        }
 +
 +                      /* Parse the reply */
                        
                        _buffer[_offset] = '\0';
                        stringstream s;
diff --combined src/lib/writer.cc
index 33817ca6eb8b6479b6eedec4539bae54c2faed91,66ddb58f7e901e0c32a14aa3f5414739cc8c1c68..489135b846e8cb7d9d20e32d00a928c8a8117256
  
  #include <fstream>
  #include <cerrno>
 -#include <libdcp/mono_picture_asset.h>
 -#include <libdcp/stereo_picture_asset.h>
 -#include <libdcp/sound_asset.h>
 -#include <libdcp/reel.h>
 -#include <libdcp/dcp.h>
 -#include <libdcp/cpl.h>
 +#include <dcp/mono_picture_mxf.h>
 +#include <dcp/stereo_picture_mxf.h>
 +#include <dcp/sound_mxf.h>
 +#include <dcp/sound_mxf_writer.h>
 +#include <dcp/reel.h>
 +#include <dcp/reel_mono_picture_asset.h>
 +#include <dcp/reel_stereo_picture_asset.h>
 +#include <dcp/reel_sound_asset.h>
 +#include <dcp/dcp.h>
 +#include <dcp/cpl.h>
  #include "writer.h"
  #include "compose.hpp"
  #include "film.h"
@@@ -41,7 -37,6 +41,7 @@@
  #include "config.h"
  #include "job.h"
  #include "cross.h"
 +#include "audio_buffers.h"
  #include "md5_digester.h"
  
  #include "i18n.h"
@@@ -61,7 -56,6 +61,7 @@@ using std::cout
  using std::stringstream;
  using boost::shared_ptr;
  using boost::weak_ptr;
 +using boost::dynamic_pointer_cast;
  
  int const Writer::_maximum_frames_in_memory = Config::instance()->num_local_encoding_threads() + 4;
  
@@@ -76,6 -70,7 +76,6 @@@ Writer::Writer (shared_ptr<const Film> 
        , _last_written_eyes (EYES_RIGHT)
        , _full_written (0)
        , _fake_written (0)
 -      , _repeat_written (0)
        , _pushed_to_disk (0)
  {
        /* Remove any old DCP */
        */
  
        if (_film->three_d ()) {
 -              _picture_asset.reset (new libdcp::StereoPictureAsset (_film->internal_video_mxf_dir (), _film->internal_video_mxf_filename ()));
 +              _picture_mxf.reset (new dcp::StereoPictureMXF (dcp::Fraction (_film->video_frame_rate (), 1)));
        } else {
 -              _picture_asset.reset (new libdcp::MonoPictureAsset (_film->internal_video_mxf_dir (), _film->internal_video_mxf_filename ()));
 +              _picture_mxf.reset (new dcp::MonoPictureMXF (dcp::Fraction (_film->video_frame_rate (), 1)));
        }
  
 -      _picture_asset->set_edit_rate (_film->video_frame_rate ());
 -      _picture_asset->set_size (_film->frame_size ());
 -      _picture_asset->set_interop (_film->interop ());
 +      _picture_mxf->set_size (_film->frame_size ());
  
        if (_film->encrypted ()) {
 -              _picture_asset->set_key (_film->key ());
 +              _picture_mxf->set_key (_film->key ());
        }
        
 -      _picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
 +      _picture_mxf_writer = _picture_mxf->start_write (
 +              _film->internal_video_mxf_dir() / _film->internal_video_mxf_filename(),
 +              _film->interop() ? dcp::INTEROP : dcp::SMPTE,
 +              _first_nonexistant_frame > 0
 +              );
  
        if (_film->audio_channels ()) {
 -              _sound_asset.reset (new libdcp::SoundAsset (_film->directory (), _film->audio_mxf_filename ()));
 -              _sound_asset->set_edit_rate (_film->video_frame_rate ());
 -              _sound_asset->set_channels (_film->audio_channels ());
 -              _sound_asset->set_sampling_rate (_film->audio_frame_rate ());
 -              _sound_asset->set_interop (_film->interop ());
 +              _sound_mxf.reset (new dcp::SoundMXF (dcp::Fraction (_film->video_frame_rate(), 1), _film->audio_frame_rate (), _film->audio_channels ()));
  
                if (_film->encrypted ()) {
 -                      _sound_asset->set_key (_film->key ());
 +                      _sound_mxf->set_key (_film->key ());
                }
 -              
 -              /* Write the sound asset into the film directory so that we leave the creation
 +      
 +              /* Write the sound MXF into the film directory so that we leave the creation
                   of the DCP directory until the last minute.
                */
 -              _sound_asset_writer = _sound_asset->start_write ();
 +              _sound_mxf_writer = _sound_mxf->start_write (_film->directory() / _film->audio_mxf_filename(), _film->interop() ? dcp::INTEROP : dcp::SMPTE);
        }
  
        _thread = new boost::thread (boost::bind (&Writer::thread, this));
@@@ -177,7 -174,7 +177,7 @@@ Writer::fake_write (int frame, Eyes eye
        }
        
        FILE* ifi = fopen_boost (_film->info_path (frame, eyes), "r");
 -      libdcp::FrameInfo info (ifi);
 +      dcp::FrameInfo info (ifi);
        fclose (ifi);
        
        QueueItem qi;
  void
  Writer::write (shared_ptr<const AudioBuffers> audio)
  {
 -      if (_sound_asset) {
 -              _sound_asset_writer->write (audio->data(), audio->frames());
 +      if (_sound_mxf_writer) {
 +              _sound_mxf_writer->write (audio->data(), audio->frames());
        }
  }
  
@@@ -241,11 -238,11 +241,11 @@@ voi
  Writer::thread ()
  try
  {
-       while (1)
+       while (true)
        {
                boost::mutex::scoped_lock lock (_mutex);
  
-               while (1) {
+               while (true) {
                        
                        if (_finish || _queued_full_in_memory > _maximum_frames_in_memory || have_sequenced_image_at_queue_head ()) {
                                /* We've got something to do: go and do it */
                                        qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, qi.eyes, false)));
                                }
  
 -                              libdcp::FrameInfo fin = _picture_asset_writer->write (qi.encoded->data(), qi.encoded->size());
 +                              dcp::FrameInfo fin = _picture_mxf_writer->write (qi.encoded->data(), qi.encoded->size());
                                qi.encoded->write_info (_film, qi.frame, qi.eyes, fin);
                                _last_written[qi.eyes] = qi.encoded;
                                ++_full_written;
                        }
                        case QueueItem::FAKE:
                                LOG_GENERAL (N_("Writer FAKE-writes %1 to MXF"), qi.frame);
 -                              _picture_asset_writer->fake_write (qi.size);
 +                              _picture_mxf_writer->fake_write (qi.size);
                                _last_written[qi.eyes].reset ();
                                ++_fake_written;
                                break;
 -                      case QueueItem::REPEAT:
 -                      {
 -                              LOG_GENERAL (N_("Writer REPEAT-writes %1 to MXF"), qi.frame);
 -                              libdcp::FrameInfo fin = _picture_asset_writer->write (
 -                                      _last_written[qi.eyes]->data(),
 -                                      _last_written[qi.eyes]->size()
 -                                      );
 -                              
 -                              _last_written[qi.eyes]->write_info (_film, qi.frame, qi.eyes, fin);
 -                              ++_repeat_written;
 -                              break;
 -                      }
                        }
                        lock.lock ();
  
                        _last_written_frame = qi.frame;
                        _last_written_eyes = qi.eyes;
                        
 -                      if (_film->length()) {
 -                              shared_ptr<Job> job = _job.lock ();
 -                              assert (job);
 -                              int total = _film->time_to_video_frames (_film->length ());
 -                              if (_film->three_d ()) {
 -                                      /* _full_written and so on are incremented for each eye, so we need to double the total
 -                                         frames to get the correct progress.
 -                                      */
 -                                      total *= 2;
 -                              }
 -                              job->set_progress (float (_full_written + _fake_written + _repeat_written) / total);
 +                      shared_ptr<Job> job = _job.lock ();
 +                      assert (job);
 +                      int64_t total = _film->length().frames (_film->video_frame_rate ());
 +                      if (_film->three_d ()) {
 +                              /* _full_written and so on are incremented for each eye, so we need to double the total
 +                                 frames to get the correct progress.
 +                              */
 +                              total *= 2;
 +                      }
 +                      if (total) {
 +                              job->set_progress (float (_full_written + _fake_written) / total);
                        }
                }
  
@@@ -382,11 -391,15 +382,11 @@@ Writer::finish (
        
        terminate_thread (true);
  
 -      _picture_asset_writer->finalize ();
 -      if (_sound_asset_writer) {
 -              _sound_asset_writer->finalize ();
 +      _picture_mxf_writer->finalize ();
 +      if (_sound_mxf_writer) {
 +              _sound_mxf_writer->finalize ();
        }
        
 -      int const frames = _last_written_frame + 1;
 -
 -      _picture_asset->set_duration (frames);
 -
        /* Hard-link the video MXF into the DCP */
        boost::filesystem::path video_from;
        video_from /= _film->internal_video_mxf_dir();
                LOG_WARNING_NC ("Hard-link failed; fell back to copying");
        }
  
 -      /* And update the asset */
 -
 -      _picture_asset->set_directory (_film->dir (_film->dcp_name ()));
 -      _picture_asset->set_file_name (_film->video_mxf_filename ());
 +      _picture_mxf->set_file (video_to);
  
        /* Move the audio MXF into the DCP */
  
 -      if (_sound_asset) {
 +      if (_sound_mxf) {
                boost::filesystem::path audio_to;
                audio_to /= _film->dir (_film->dcp_name ());
                audio_to /= _film->audio_mxf_filename ();
                                String::compose (_("could not move audio MXF into the DCP (%1)"), ec.value ()), _film->file (_film->audio_mxf_filename ())
                                );
                }
 -              
 -              _sound_asset->set_directory (_film->dir (_film->dcp_name ()));
 -              _sound_asset->set_duration (frames);
 +
 +              _sound_mxf->set_file (audio_to);
        }
 -      
 -      libdcp::DCP dcp (_film->dir (_film->dcp_name()));
  
 -      shared_ptr<libdcp::CPL> cpl (
 -              new libdcp::CPL (
 -                      _film->dir (_film->dcp_name()),
 +      dcp::DCP dcp (_film->dir (_film->dcp_name()));
 +
 +      shared_ptr<dcp::CPL> cpl (
 +              new dcp::CPL (
                        _film->dcp_name(),
 -                      _film->dcp_content_type()->libdcp_kind (),
 -                      frames,
 -                      _film->video_frame_rate ()
 +                      _film->dcp_content_type()->libdcp_kind ()
                        )
                );
        
 -      dcp.add_cpl (cpl);
 +      dcp.add (cpl);
  
 -      cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
 -                                                       _picture_asset,
 -                                                       _sound_asset,
 -                                                       shared_ptr<libdcp::SubtitleAsset> ()
 -                                                       )
 -                             ));
 +      shared_ptr<dcp::Reel> reel (new dcp::Reel ());
 +
 +      shared_ptr<dcp::MonoPictureMXF> mono = dynamic_pointer_cast<dcp::MonoPictureMXF> (_picture_mxf);
 +      if (mono) {
 +              reel->add (shared_ptr<dcp::ReelPictureAsset> (new dcp::ReelMonoPictureAsset (mono, 0)));
 +              dcp.add (mono);
 +      }
 +
 +      shared_ptr<dcp::StereoPictureMXF> stereo = dynamic_pointer_cast<dcp::StereoPictureMXF> (_picture_mxf);
 +      if (stereo) {
 +              reel->add (shared_ptr<dcp::ReelPictureAsset> (new dcp::ReelStereoPictureAsset (stereo, 0)));
 +              dcp.add (stereo);
 +      }
 +
 +      if (_sound_mxf) {
 +              reel->add (shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (_sound_mxf, 0)));
 +              dcp.add (_sound_mxf);
 +      }
 +      
 +      cpl->add (reel);
  
        shared_ptr<Job> job = _job.lock ();
        assert (job);
  
        job->sub (_("Computing image digest"));
 -      _picture_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
 +      _picture_mxf->hash (boost::bind (&Job::set_progress, job.get(), _1, false));
  
 -      if (_sound_asset) {
 +      if (_sound_mxf) {
                job->sub (_("Computing audio digest"));
 -              _sound_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
 +              _sound_mxf->hash (boost::bind (&Job::set_progress, job.get(), _1, false));
        }
  
 -      libdcp::XMLMetadata meta = Config::instance()->dcp_metadata ();
 +      dcp::XMLMetadata meta = Config::instance()->dcp_metadata ();
        meta.set_issue_date_now ();
 -      dcp.write_xml (_film->interop (), meta, _film->is_signed() ? make_signer () : shared_ptr<const libdcp::Signer> ());
 +
 +      dcp.write_xml (_film->interop () ? dcp::INTEROP : dcp::SMPTE, meta, _film->is_signed() ? make_signer () : shared_ptr<const dcp::Signer> ());
  
        LOG_GENERAL (
 -              N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT; %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk
 +              N_("Wrote %1 FULL, %2 FAKE, %3 pushed to disk"), _full_written, _fake_written, _pushed_to_disk
                );
  }
  
 -/** Tell the writer that frame `f' should be a repeat of the frame before it */
 -void
 -Writer::repeat (int f, Eyes e)
 -{
 -      boost::mutex::scoped_lock lock (_mutex);
 -
 -      while (_queued_full_in_memory > _maximum_frames_in_memory) {
 -              /* The queue is too big; wait until that is sorted out */
 -              _full_condition.wait (lock);
 -      }
 -      
 -      QueueItem qi;
 -      qi.type = QueueItem::REPEAT;
 -      qi.frame = f;
 -      if (_film->three_d() && e == EYES_BOTH) {
 -              qi.eyes = EYES_LEFT;
 -              _queue.push_back (qi);
 -              qi.eyes = EYES_RIGHT;
 -              _queue.push_back (qi);
 -      } else {
 -              qi.eyes = e;
 -              _queue.push_back (qi);
 -      }
 -
 -      /* Now there's something to do: wake anything wait()ing on _empty_condition */
 -      _empty_condition.notify_all ();
 -}
 -
  bool
  Writer::check_existing_picture_mxf_frame (FILE* mxf, int f, Eyes eyes)
  {
                return false;
        }
        
 -      libdcp::FrameInfo info (ifi);
 +      dcp::FrameInfo info (ifi);
        fclose (ifi);
        if (info.size == 0) {
                LOG_GENERAL ("Existing frame %1 has no info file", f);
@@@ -530,7 -563,7 +530,7 @@@ Writer::check_existing_picture_mxf (
                ++N;
        }
  
-       while (1) {
+       while (true) {
  
                shared_ptr<Job> job = _job.lock ();
                assert (job);
diff --combined src/tools/dcpomatic.cc
index 1fb7feb7de2a8da436650af4f78de93be7101c57,aeb62a44e5f2cfd2b7cf5c92511fd2bec83b69a8..fb63ae9971c35b5839b2b2ec6ebba713115efcc5
@@@ -30,7 -30,7 +30,7 @@@
  #include <wx/stdpaths.h>
  #include <wx/cmdline.h>
  #include <wx/preferences.h>
 -#include <libdcp/exceptions.h>
 +#include <dcp/exceptions.h>
  #include "wx/film_viewer.h"
  #include "wx/film_editor.h"
  #include "wx/job_manager_view.h"
@@@ -392,7 -392,7 +392,7 @@@ private
                        );
                
                int r;
-               while (1) {
+               while (true) {
                        r = c->ShowModal ();
                        if (r == wxID_OK && c->GetPath() == wxStandardPaths::Get().GetDocumentsDir()) {
                                error_dialog (this, _("You did not select a folder.  Make sure that you select a folder before clicking Open."));
                                        shared_ptr<Job> (new SendKDMEmailJob (film, d->screens (), d->cpl (), d->from (), d->until (), d->formulation ()))
                                        );
                        }
 -              } catch (libdcp::NotEncryptedError& e) {
 +              } catch (dcp::NotEncryptedError& e) {
                        error_dialog (this, _("CPL's content is not encrypted."));
                } catch (exception& e) {
                        error_dialog (this, e.what ());
@@@ -643,9 -643,6 +643,9 @@@ static const wxCmdLineEntryDesc command
        { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 }
  };
  
 +/** @class App
 + *  @brief The magic App class for wxWidgets.
 + */
  class App : public wxApp
  {
        bool OnInit ()
index acd1335e62d5e5ef57c469232e429db29c22e6c6,ff948e9fbbae1472089acfc10594c0c914c03f0e..7dd820b9571bc9becf931b176aabcd975e75d996
@@@ -20,7 -20,7 +20,7 @@@
  #include <iostream>
  #include <iomanip>
  #include <getopt.h>
 -#include <libdcp/version.h>
 +#include <dcp/version.h>
  #include "lib/film.h"
  #include "lib/filter.h"
  #include "lib/transcode_job.h"
@@@ -69,7 -69,7 +69,7 @@@ main (int argc, char* argv[]
        bool keep_going = false;
  
        int option_index = 0;
-       while (1) {
+       while (true) {
                static struct option long_options[] = {
                        { "version", no_argument, 0, 'v'},
                        { "help", no_argument, 0, 'h'},
        }
  
        if (keep_going) {
-               while (1) {
+               while (true) {
                        dcpomatic_sleep (3600);
                }
        }
index b0a67c6d93d5630fc919731ff251114c292f2690,26de1c71f8f2485bf273135f110bca61dc9a1bb6..304f4f697ab6e8a031c44dd2a74c3b896f2c448a
@@@ -81,7 -81,7 +81,7 @@@ main (int argc, char* argv[]
        boost::filesystem::path output;
        
        int option_index = 0;
-       while (1) {
+       while (true) {
                static struct option long_options[] = {
                        { "version", no_argument, 0, 'v'},
                        { "help", no_argument, 0, 'h'},
                for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
                        shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (*i);
                        if (ic) {
 -                              ic->set_video_length (still_length * 24);
 +                              ic->set_video_length (ContentTime::from_seconds (still_length));
                        }
                }
  
index fa14dd141ad03f210380e76fa1245b534e6d5b96,092a1ca1b0725027a5026491ea1efb54b93a7866..2ad07cce7a38bae73c62f2b1824def725e9ada34
@@@ -18,7 -18,7 +18,7 @@@
  */
  
  #include <getopt.h>
 -#include <libdcp/certificates.h>
 +#include <dcp/certificates.h>
  #include "lib/film.h"
  #include "lib/cinema.h"
  #include "lib/kdm.h"
@@@ -41,8 -41,8 +41,8 @@@ help (
        cerr << "Syntax: " << program_name << " [OPTION] [<FILM>]\n"
                "  -h, --help             show this help\n"
                "  -o, --output           output file or directory\n"
 -              "  -f, --valid-from       valid from time (e.g. \"2013-09-28 01:41:51\") or \"now\"\n"
 -              "  -t, --valid-to         valid to time (e.g. \"2014-09-28 01:41:51\")\n"
 +              "  -f, --valid-from       valid from time (in local time zone) (e.g. \"2013-09-28 01:41:51\") or \"now\"\n"
 +              "  -t, --valid-to         valid to time (in local time zone) (e.g. \"2014-09-28 01:41:51\")\n"
                "  -d, --valid-duration   valid duration (e.g. \"1 day\", \"4 hours\", \"2 weeks\")\n"
                "      --formulation      modified-transitional-1, dci-any or dci-specific [default modified-transitional-1]\n"
                "  -z, --zip              ZIP each cinema's KDMs into its own file\n"
@@@ -111,12 -111,12 +111,12 @@@ int main (int argc, char* argv[]
        bool cinemas = false;
        string duration_string;
        bool verbose = false;
 -      libdcp::KDM::Formulation formulation = libdcp::KDM::MODIFIED_TRANSITIONAL_1;
 +      dcp::Formulation formulation = dcp::MODIFIED_TRANSITIONAL_1;
  
        program_name = argv[0];
        
        int option_index = 0;
-       while (1) {
+       while (true) {
                static struct option long_options[] = {
                        { "help", no_argument, 0, 'h'},
                        { "output", required_argument, 0, 'o'},
                        break;
                case 'C':
                        if (string (optarg) == "modified-transitional-1") {
 -                              formulation = libdcp::KDM::MODIFIED_TRANSITIONAL_1;
 +                              formulation = dcp::MODIFIED_TRANSITIONAL_1;
                        } else if (string (optarg) == "dci-any") {
 -                              formulation = libdcp::KDM::DCI_ANY;
 +                              formulation = dcp::DCI_ANY;
                        } else if (string (optarg) == "dci-specific") {
 -                              formulation = libdcp::KDM::DCI_SPECIFIC;
 +                              formulation = dcp::DCI_SPECIFIC;
                        } else {
                                error ("unrecognised KDM formulation " + formulation);
                        }
                        error ("you must specify --output");
                }
                
 -              shared_ptr<libdcp::Certificate> certificate (new libdcp::Certificate (boost::filesystem::path (certificate_file)));
 -              libdcp::KDM kdm = film->make_kdm (certificate, cpl, valid_from.get(), valid_to.get(), formulation);
 +              shared_ptr<dcp::Certificate> certificate (new dcp::Certificate (boost::filesystem::path (certificate_file)));
 +              dcp::EncryptedKDM kdm = film->make_kdm (certificate, cpl, valid_from.get(), valid_to.get(), formulation);
                kdm.as_xml (output);
                if (verbose) {
                        cout << "Generated KDM " << output << " for certificate.\n";
  
                try {
                        if (zip) {
 -                              write_kdm_zip_files (film, (*i)->screens(), cpl, valid_from.get(), valid_to.get(), formulation, output);
 +                              write_kdm_zip_files (
 +                                      film, (*i)->screens(), cpl, dcp::LocalTime (valid_from.get()), dcp::LocalTime (valid_to.get()), formulation, output
 +                                      );
 +                              
                                if (verbose) {
                                        cout << "Wrote ZIP files to " << output << "\n";
                                }
                        } else {
 -                              write_kdm_files (film, (*i)->screens(), cpl, valid_from.get(), valid_to.get(), formulation, output);
 +                              write_kdm_files (
 +                                      film, (*i)->screens(), cpl, dcp::LocalTime (valid_from.get()), dcp::LocalTime (valid_to.get()), formulation, output
 +                                      );
 +                              
                                if (verbose) {
                                        cout << "Wrote KDM files to " << output << "\n";
                                }
diff --combined src/tools/server_test.cc
index 3c2ea4b36068ecbde2eae65658cc04b75d513536,a5d31fc086444e70c3bc07b11235df356ea04d3e..5997caab67c61b86212d3f010fb5445fcf086d69
@@@ -45,18 -45,18 +45,18 @@@ using boost::shared_ptr
  static shared_ptr<Film> film;
  static ServerDescription* server;
  static shared_ptr<FileLog> log_ (new FileLog ("servomatictest.log"));
 -static int frame = 0;
 +static int frame_count = 0;
  
  void
  process_video (shared_ptr<PlayerVideoFrame> pvf)
  {
 -      shared_ptr<DCPVideoFrame> local  (new DCPVideoFrame (pvf, frame, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
 -      shared_ptr<DCPVideoFrame> remote (new DCPVideoFrame (pvf, frame, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
 +      shared_ptr<DCPVideoFrame> local  (new DCPVideoFrame (pvf, frame_count, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
 +      shared_ptr<DCPVideoFrame> remote (new DCPVideoFrame (pvf, frame_count, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
  
 -      cout << "Frame " << frame << ": ";
 +      cout << "Frame " << frame_count << ": ";
        cout.flush ();
  
 -      ++frame;
 +      ++frame_count;
  
        shared_ptr<EncodedData> local_encoded = local->encode_locally ();
        shared_ptr<EncodedData> remote_encoded;
@@@ -103,7 -103,7 +103,7 @@@ main (int argc, char* argv[]
        string film_dir;
        string server_host;
  
-       while (1) {
+       while (true) {
                static struct option long_options[] = {
                        { "help", no_argument, 0, 'h'},
                        { "server", required_argument, 0, 's'},
                film->read_metadata ();
                
                shared_ptr<Player> player = film->make_player ();
 -              player->disable_audio ();
  
 -              player->Video.connect (boost::bind (process_video, _1));
 -              bool done = false;
 -              while (!done) {
 -                      done = player->pass ();
 +              DCPTime const frame = DCPTime::from_frames (1, film->video_frame_rate ());
 +              for (DCPTime t; t < film->length(); t += frame) {
 +                      process_video (player->get_video(t, true).front ());
                }
        } catch (std::exception& e) {
                cerr << "Error: " << e.what() << "\n";
diff --combined src/wx/film_editor.cc
index 0c70d4b3efc3d12c092dddda5ab72be55e970b58,854877ecec86d1e063b982a95dc6fc9222facac6..ee8ee6d389346598f3e013eeb236c816451eedbd
@@@ -439,6 -439,9 +439,6 @@@ FilmEditor::film_changed (Film::Propert
                checked_set (_name, _film->name());
                setup_dcp_name ();
                break;
 -      case Film::WITH_SUBTITLES:
 -              setup_dcp_name ();
 -              break;
        case Film::DCP_CONTENT_TYPE:
                checked_set (_dcp_content_type, DCPContentType::as_index (_film->dcp_content_type ()));
                setup_dcp_name ();
@@@ -525,7 -528,7 +525,7 @@@ FilmEditor::film_content_changed (int p
                (*i)->film_content_changed (property);
        }
  
 -      if (property == FFmpegContentProperty::AUDIO_STREAM) {
 +      if (property == FFmpegContentProperty::AUDIO_STREAM || property == SubtitleContentProperty::SUBTITLE_USE) {
                setup_dcp_name ();
        } else if (property == ContentProperty::PATH) {
                setup_content ();
@@@ -614,6 -617,7 +614,6 @@@ FilmEditor::set_film (shared_ptr<Film> 
        film_changed (Film::CONTAINER);
        film_changed (Film::RESOLUTION);
        film_changed (Film::SCALER);
 -      film_changed (Film::WITH_SUBTITLES);
        film_changed (Film::SIGNED);
        film_changed (Film::ENCRYPTED);
        film_changed (Film::J2K_BANDWIDTH);
@@@ -867,7 -871,7 +867,7 @@@ FilmEditor::setup_content_sensitivity (
  
        _video_panel->Enable    (video_selection.size() > 0 && _generally_sensitive);
        _audio_panel->Enable    (audio_selection.size() > 0 && _generally_sensitive);
 -      _subtitle_panel->Enable (selection.size() == 1 && dynamic_pointer_cast<FFmpegContent> (selection.front()) && _generally_sensitive);
 +      _subtitle_panel->Enable (selection.size() == 1 && dynamic_pointer_cast<SubtitleContent> (selection.front()) && _generally_sensitive);
        _timing_panel->Enable   (selection.size() == 1 && _generally_sensitive);
  }
  
@@@ -876,7 -880,7 +876,7 @@@ FilmEditor::selected_content (
  {
        ContentList sel;
        long int s = -1;
-       while (1) {
+       while (true) {
                s = _content->GetNextItem (s, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
                if (s == -1) {
                        break;
diff --combined test/job_test.cc
index 2022a8ddadf0ab5d464244b7b0124d6d1294f45b,a77bc949ce123e7e851ea23d038315039476b1a9..c1b66d4e01f38f36127c49087c79473cbdb20739
@@@ -1,5 -1,5 +1,5 @@@
  /*
 -    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
 +    Copyright (C) 2012-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
  
  */
  
 +/** @file  test/job_test.cc
 + *  @brief Basic tests of Job and JobManager.
 + */
 +
  #include <boost/test/unit_test.hpp>
  #include "lib/job.h"
  #include "lib/job_manager.h"
@@@ -48,7 -44,7 +48,7 @@@ public
  
        void run ()
        {
-               while (1) {
+               while (true) {
                        if (finished ()) {
                                return;
                        }