Merge master.
authorCarl Hetherington <cth@carlh.net>
Sun, 27 Apr 2014 22:41:26 +0000 (23:41 +0100)
committerCarl Hetherington <cth@carlh.net>
Sun, 27 Apr 2014 22:41:26 +0000 (23:41 +0100)
1  2 
ChangeLog
cscript
src/lib/image_content.cc
src/lib/job.cc
src/lib/piece.h
src/lib/player.h
src/lib/util.cc
src/wx/film_editor.cc

diff --combined ChangeLog
index 04eb830f5e7451aef832fbf8e2d458dd6be9e2ab,c30de54f8e0ba8dbe2e0a3f9449eebb7565aab88..1bbaed6e5222024718f96bbe4e6c900e3625d9c1
+++ b/ChangeLog
@@@ -1,7 -1,46 +1,50 @@@
 +2014-03-07  Carl Hetherington  <cth@carlh.net>
 +
 +      * Add subtitle view.
 +
+ 2014-04-27  Carl Hetherington  <cth@carlh.net>
+       * Version 1.66.16 released.
+ 2014-04-27  Carl Hetherington  <cth@carlh.net>
+       * Add .dpx to the list of acceptable image files.
+       * Slightly better handling of uncaught exceptions.
+       * Use our own directory picker on 14.04 (as well as 13.04 and 13.10) as
+       it appears that the same bug remains.
+ 2014-04-25  Carl Hetherington  <cth@carlh.net>
+       * Version 1.66.15 released.
+ 2014-04-25  Carl Hetherington  <cth@carlh.net>
+       * Fix subtitle display when the next subtitle is decoded before the previous
+       one has finished.
+ 2014-04-24  Carl Hetherington  <cth@carlh.net>
+       * Version 1.66.14 released.
+ 2014-04-23  Carl Hetherington  <cth@carlh.net>
+       * Version 1.66.13 released.
+ 2014-04-21  Carl Hetherington  <cth@carlh.net>
+       * Update to es_ES translation from Manuel AC.
+       * Update to fr_FR translation from Thierry Journet.
+ 2014-04-17  Carl Hetherington  <cth@carlh.net>
+       * Fix update of the gain control when using the gain calculator
+       dialog.
+       * Version 1.66.12 released.
  2014-04-07  Carl Hetherington  <cth@carlh.net>
  
        * Version 1.66.11 released.
  
        * Another attempt to fix colour conversion dialog strange behaviour
        on OS X.
 +>>>>>>> master
  
  2014-03-18  Carl Hetherington  <cth@carlh.net>
  
diff --combined cscript
index c07c430310a41ad3f430e1bc60ba8d0123fefe50,142f1c287da23de4bd1ace9a1e23472c6a9757a3..890c937e6f3d15e8be71bbe7b96418fdc017a027
+++ b/cscript
@@@ -71,6 -71,20 +71,20 @@@ deb_depends['13.10'] = {'libc6': '2.17-
                          'libcurl3': '7.29.0-1ubuntu3',
                          'libzip2': '0.10.1-1.1'}
  
+ deb_depends['14.04'] = {'libc6': '2.19-0ubuntu6',
+                         'libssh-4': '0.6.1-0ubuntu3',
+                         'libboost-filesystem1.54.0': '1.54.0-4ubuntu3',
+                         'libboost-thread1.54.0': '1.54.0-4ubuntu3',
+                         'libsndfile1': '1.0.25-7ubuntu2',
+                         'libmagick++5': '8:6.7.7.10-6ubuntu3',
+                         'libxml++2.6-2': '2.36.0-2ubuntu1',
+                         'libgtk2.0-0': '2.24.23-0ubuntu1',
+                         'libxmlsec1': '1.2.18-2ubuntu1',
+                         'libxmlsec1-openssl': '1.2.18-2ubuntu1',
+                         'libboost-date-time1.54.0': '1.54.0-4ubuntu3',
+                         'libcurl3': '7.35.0-1ubuntu2',
+                         'libzip2': '0.10.1-1.2'}
  deb_depends['7'] = {'libc6': '2.13',
                      'libssh-4': '0.5.4',
                      'libboost-filesystem1.49.0': '1.49.0',
@@@ -130,7 -144,7 +144,7 @@@ def make_control(debian_version, bits, 
  
  def dependencies(target):
      return (('ffmpeg-cdist', 'a0db025'),
 -            ('libdcp', '8af7b48'))
 +            ('libdcp', '1.0'))
  
  def build(target, options):
      cmd = './waf configure --prefix=%s' % target.work_dir_cscript()
diff --combined src/lib/image_content.cc
index d7b37a8353fa5f04b63c58006a2a6c8eade95a1f,3b87fcf008c4372114e77009e424dbdd30dd5cf6..56c83d3f740c3343903ec6f02ed517681fc69a3d
@@@ -44,7 -44,11 +44,11 @@@ ImageContent::ImageContent (shared_ptr<
                                _paths.push_back (i->path ());
                        }
                }
-               
+               if (_paths.empty()) {
+                       throw FileError (_("No valid image files were found in the folder."), p);
+               }
+                               
                sort (_paths.begin(), _paths.end());
        }
  }
@@@ -110,7 -114,7 +114,7 @@@ ImageContent::examine (shared_ptr<Job> 
  }
  
  void
 -ImageContent::set_video_length (VideoContent::Frame len)
 +ImageContent::set_video_length (ContentTime len)
  {
        {
                boost::mutex::scoped_lock lm (_mutex);
        signal_changed (ContentProperty::LENGTH);
  }
  
 -Time
 +DCPTime
  ImageContent::full_length () const
  {
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
 -      
 -      FrameRateConversion frc (video_frame_rate(), film->video_frame_rate ());
 -      return video_length_after_3d_combine() * frc.factor() * TIME_HZ / video_frame_rate();
 +      return DCPTime (video_length_after_3d_combine(), FrameRateChange (video_frame_rate(), film->video_frame_rate()));
  }
  
  string
@@@ -133,7 -139,7 +137,7 @@@ ImageContent::identifier () cons
  {
        stringstream s;
        s << VideoContent::identifier ();
 -      s << "_" << video_length();
 +      s << "_" << video_length().get();
        return s.str ();
  }
  
diff --combined src/lib/job.cc
index 3639e52830ed9fa585f62cbd9a66df8f80891cee,96aedac65bf4fb8718d8749712db3ae28d9ae627..c6a6b90a808fcdd234c053ce1c21a31d3128d3eb
@@@ -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
  
  #include <boost/thread.hpp>
  #include <boost/filesystem.hpp>
 -#include <libdcp/exceptions.h>
 +#include <dcp/exceptions.h>
  #include "job.h"
  #include "util.h"
  #include "cross.h"
  #include "ui_signaller.h"
  #include "exceptions.h"
 +#include "film.h"
 +#include "log.h"
  
  #include "i18n.h"
  
@@@ -68,8 -66,8 +68,8 @@@ Job::run_wrapper (
  
                run ();
  
 -      } catch (libdcp::FileError& e) {
 -              
 +      } catch (dcp::FileError& e) {
 +
                string m = String::compose (_("An error occurred whilst handling the file %1."), boost::filesystem::path (e.filename()).leaf());
  
                try {
  
                set_error (
                        e.what (),
-                       _("It is not known what caused this error.  The best idea is to report the problem to the DCP-o-matic mailing list (carl@dcpomatic.com)")
+                       _("It is not known what caused this error.  Please report the problem to the DCP-o-matic author (carl@dcpomatic.com).")
                        );
  
                set_progress (1);
  
                set_error (
                        _("Unknown error"),
-                       _("It is not known what caused this error.  The best idea is to report the problem to the DCP-o-matic mailing list (carl@dcpomatic.com)")
+                       _("It is not known what caused this error.  Please report the problem to the DCP-o-matic author (carl@dcpomatic.com).")
                        );
  
                set_progress (1);
@@@ -206,7 -204,7 +206,7 @@@ Job::set_state (State s
        }       
  }
  
 -/** @return Time (in seconds) that this sub-job has been running */
 +/** @return DCPTime (in seconds) that this sub-job has been running */
  int
  Job::elapsed_time () const
  {
@@@ -281,7 -279,6 +281,7 @@@ Job::error_summary () cons
  void
  Job::set_error (string s, string d)
  {
 +      _film->log()->log (String::compose ("Error in job: %1 (%2)", s, d));
        boost::mutex::scoped_lock lm (_state_mutex);
        _error_summary = s;
        _error_details = d;
diff --combined src/lib/piece.h
index 0000000000000000000000000000000000000000,76df909ff361ba8c43e48ee42097071e50f1424e..976409381d3a8744c69f5f14e53f67a5ca688e08
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,65 +1,43 @@@
 -class Piece;
 -class Image;
 -class Player;
 -
 -struct IncomingVideo
 -{
 -public:
 -      boost::weak_ptr<Piece> weak_piece;
 -      boost::shared_ptr<const Image> image;
 -      Eyes eyes;
 -      bool same;
 -      VideoContent::Frame frame;
 -      Time extra;
 -};
+ /*
+     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_PIECE_H
+ #define DCPOMATIC_PIECE_H
+ #include "types.h"
+ #include "video_content.h"
+ class Content;
+ class Decoder;
 -      Piece (boost::shared_ptr<Content> c);
 -      Piece (boost::shared_ptr<Content> c, boost::shared_ptr<Decoder> d);
 -      void set_repeat (IncomingVideo video, int num);
 -      void reset_repeat ();
 -      bool repeating () const;
 -      void repeat (Player* player);
 -
+ class Piece
+ {
+ public:
 -      /** Time of the last video we emitted relative to the start of the DCP */
 -      Time video_position;
 -      /** Time of the last audio we emitted relative to the start of the DCP */
 -      Time audio_position;
 -
 -      IncomingVideo repeat_video;
 -      int repeat_to_do;
 -      int repeat_done;
++      Piece (boost::shared_ptr<Content> c, boost::shared_ptr<Decoder> d, FrameRateChange f)
++              : content (c)
++              , decoder (d)
++              , frc (f)
++      {}
++      
+       boost::shared_ptr<Content> content;
+       boost::shared_ptr<Decoder> decoder;
++      FrameRateChange frc;
+ };
+ #endif
diff --combined src/lib/player.h
index d83045ab12a6b8e84478be51188111240095f971,4d911a83be3f4ecd9daa715a2aef3848bba025a8..62ba89e6c6e783c2603a390f41d43d1583c079af
  #include "content.h"
  #include "film.h"
  #include "rect.h"
 -#include "audio_merger.h"
  #include "audio_content.h"
 -#include "subtitle.h"
 +#include "dcpomatic_time.h"
 +#include "content_subtitle.h"
 +#include "position_image.h"
+ #include "piece.h"
  
  class Job;
  class Film;
@@@ -38,74 -38,64 +39,82 @@@ class Playlist
  class AudioContent;
  class Piece;
  class Image;
 -class Resampler;
 +class DCPVideo;
 +class Decoder;
 +
 +class PlayerStatistics
 +{
 +public:
 +      struct Video {
 +              Video ()
 +                      : black (0)
 +                      , repeat (0)
 +                      , good (0)
 +                      , skip (0)
 +              {}
 +              
 +              int black;
 +              int repeat;
 +              int good;
 +              int skip;
 +      } video;
 +
 +      struct Audio {
 +              Audio ()
 +                      : silence (0)
 +                      , good (0)
 +                      , skip (0)
 +              {}
 +              
 +              DCPTime silence;
 +              int64_t good;
 +              int64_t skip;
 +      } audio;
 +
 +      void dump (boost::shared_ptr<Log>) const;
 +};
  
- class Piece
+ /** A wrapper for an Image which contains some pending operations; these may
+  *  not be necessary if the receiver of the PlayerImage throws it away.
+  */
+ class PlayerImage
  {
  public:
-       Piece (boost::shared_ptr<Content> c, boost::shared_ptr<Decoder> d, FrameRateChange f)
-               : content (c)
-               , decoder (d)
-               , frc (f)
-       {}
-       boost::shared_ptr<Content> content;
-       boost::shared_ptr<Decoder> decoder;
-       FrameRateChange frc;
 -      PlayerImage (boost::shared_ptr<const Image>, Crop, libdcp::Size, libdcp::Size, Scaler const *);
++      PlayerImage (boost::shared_ptr<const Image>, Crop, dcp::Size, dcp::Size, Scaler const *);
+       void set_subtitle (boost::shared_ptr<const Image>, Position<int>);
+       
+       boost::shared_ptr<Image> image ();
 -
++      
+ private:
+       boost::shared_ptr<const Image> _in;
+       Crop _crop;
 -      libdcp::Size _inter_size;
 -      libdcp::Size _out_size;
++      dcp::Size _inter_size;
++      dcp::Size _out_size;
+       Scaler const * _scaler;
+       boost::shared_ptr<const Image> _subtitle_image;
+       Position<int> _subtitle_position;
  };
 - 
 +
  /** @class Player
 - *  @brief A class which can `play' a Playlist; emitting its audio and video.
 + *  @brief A class which can `play' a Playlist.
   */
  class Player : public boost::enable_shared_from_this<Player>, public boost::noncopyable
  {
  public:
        Player (boost::shared_ptr<const Film>, boost::shared_ptr<const Playlist>);
  
 -      void disable_video ();
 -      void disable_audio ();
 -
 -      bool pass ();
 -      void seek (Time, bool);
 +      boost::shared_ptr<DCPVideo> get_video (DCPTime time, bool accurate);
 +      boost::shared_ptr<AudioBuffers> get_audio (DCPTime time, DCPTime length, bool accurate);
  
 -      Time video_position () const {
 -              return _video_position;
 +      void set_video_container_size (dcp::Size);
 +      void set_approximate_size ();
 +      void set_burn_subtitles (bool burn) {
 +              _burn_subtitles = burn;
        }
  
 -      void set_video_container_size (libdcp::Size);
 -
 -      bool repeat_last_video ();
 -
 -      /** Emitted when a video frame is ready.
 -       *  First parameter is the video image.
 -       *  Second parameter is the eye(s) that should see this image.
 -       *  Third parameter is the colour conversion that should be used for this image.
 -       *  Fourth parameter is true if the image is the same as the last one that was emitted.
 -       *  Fifth parameter is the time.
 -       */
 -      boost::signals2::signal<void (boost::shared_ptr<PlayerImage>, Eyes, ColourConversion, bool, Time)> Video;
 +      PlayerStatistics const & statistics () const;
        
 -      /** Emitted when some audio data is ready */
 -      boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, Time)> Audio;
 -
        /** Emitted when something has changed such that if we went back and emitted
         *  the last frame again it would look different.  This is not emitted after
         *  a seek.
@@@ -118,49 -108,50 +127,49 @@@ private
        friend class PlayerWrapper;
        friend class Piece;
  
 -      void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const Image>, Eyes, bool, VideoContent::Frame, Time);
 -      void process_audio (boost::weak_ptr<Piece>, boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
 -      void process_subtitle (boost::weak_ptr<Piece>, boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time);
        void setup_pieces ();
        void playlist_changed ();
        void content_changed (boost::weak_ptr<Content>, int, bool);
 -      void do_seek (Time, bool);
        void flush ();
 -      void emit_black ();
 -      void emit_silence (OutputAudioFrame);
 -      boost::shared_ptr<Resampler> resampler (boost::shared_ptr<AudioContent>, bool);
        void film_changed (Film::Property);
 -      void update_subtitle ();
 -
 +      std::list<PositionImage> process_content_image_subtitles (
 +              boost::shared_ptr<SubtitleContent>, std::list<boost::shared_ptr<ContentImageSubtitle> >
 +              );
 +      std::list<PositionImage> process_content_text_subtitles (std::list<boost::shared_ptr<ContentTextSubtitle> >);
 +      void update_subtitle_from_text ();
 +      VideoFrame dcp_to_content_video (boost::shared_ptr<const Piece> piece, DCPTime t) const;
 +      AudioFrame dcp_to_content_audio (boost::shared_ptr<const Piece> piece, DCPTime t) const;
 +      ContentTime dcp_to_content_subtitle (boost::shared_ptr<const Piece> piece, DCPTime t) const;
 +      boost::shared_ptr<DCPVideo> black_dcp_video (DCPTime) const;
 +
 +      template<class C>
 +      std::list<boost::shared_ptr<Piece> >
 +      overlaps (DCPTime t)
 +      {
 +              std::list<boost::shared_ptr<Piece> > overlaps;
 +              for (typename std::list<boost::shared_ptr<Piece> >::const_iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
 +                      if (boost::dynamic_pointer_cast<C> ((*i)->content) && (*i)->content->position() <= t && t < (*i)->content->end()) {
 +                              overlaps.push_back (*i);
 +                      }
 +              }
 +              
 +              return overlaps;
 +      }
 +      
        boost::shared_ptr<const Film> _film;
        boost::shared_ptr<const Playlist> _playlist;
 -      
 -      bool _video;
 -      bool _audio;
  
        /** Our pieces are ready to go; if this is false the pieces must be (re-)created before they are used */
        bool _have_valid_pieces;
        std::list<boost::shared_ptr<Piece> > _pieces;
  
 -      /** The time after the last video that we emitted */
 -      Time _video_position;
 -      /** The time after the last audio that we emitted */
 -      Time _audio_position;
 -
 -      AudioMerger<Time, AudioContent::Frame> _audio_merger;
 -
 -      libdcp::Size _video_container_size;
 -      boost::shared_ptr<PlayerImage> _black_frame;
 -      std::map<boost::shared_ptr<AudioContent>, boost::shared_ptr<Resampler> > _resamplers;
 -
 -      std::list<Subtitle> _subtitles;
 -
 -#ifdef DCPOMATIC_DEBUG
 -      boost::shared_ptr<Content> _last_video;
 -#endif
 +      dcp::Size _video_container_size;
 +      boost::shared_ptr<Image> _black_image;
  
 -      bool _last_emit_was_black;
 +      bool _approximate_size;
 +      bool _burn_subtitles;
  
 -      IncomingVideo _last_incoming_video;
 +      PlayerStatistics _statistics;
  
        boost::signals2::scoped_connection _playlist_changed_connection;
        boost::signals2::scoped_connection _playlist_content_changed_connection;
diff --combined src/lib/util.cc
index 40e9d9c2eed730795a8f8476533ae16290c403c0,f15d2283c4cfb2d1bbb1066d582a38c36b2a5511..c23deb59e2f46ddb9f2021442e9e7b4d4df264de
@@@ -1,5 -1,5 +1,5 @@@
  /*
 -    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
 +    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
      Copyright (C) 2000-2007 Paul Davis
  
      This program is free software; you can redistribute it and/or modify
  #include <glib.h>
  #include <openjpeg.h>
  #include <openssl/md5.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 <dcp/version.h>
 +#include <dcp/util.h>
 +#include <dcp/signer_chain.h>
 +#include <dcp/signer.h>
  extern "C" {
  #include <libavcodec/avcodec.h>
  #include <libavformat/avformat.h>
@@@ -71,7 -70,6 +71,7 @@@
  #include "job.h"
  #include "cross.h"
  #include "video_content.h"
 +#include "rect.h"
  #ifdef DCPOMATIC_WINDOWS
  #include "stack.hpp"
  #endif
@@@ -103,7 -101,7 +103,7 @@@ using boost::shared_ptr
  using boost::thread;
  using boost::lexical_cast;
  using boost::optional;
 -using libdcp::Size;
 +using dcp::Size;
  
  static boost::thread::id ui_thread;
  static boost::filesystem::path backtrace_file;
@@@ -239,6 -237,24 +239,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 ()
 -{
 -      stringstream 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)
  {
@@@ -325,8 -341,7 +325,8 @@@ dcpomatic_setup (
  
        set_terminate (terminate);
  
 -      libdcp::init ();
 +      Pango::init ();
 +      dcp::init ();
        
        Ratio::setup_ratios ();
        VideoContentScale::setup_scales ();
@@@ -475,6 -490,33 +475,6 @@@ md5_digest (vector<boost::filesystem::p
        return s.str ();
  }
  
 -static bool
 -about_equal (float a, float b)
 -{
 -      /* A film of F seconds at f FPS will be Ff frames;
 -         Consider some delta FPS d, so if we run the same
 -         film at (f + d) FPS it will last F(f + d) seconds.
 -
 -         Hence the difference in length over the length of the film will
 -         be F(f + d) - Ff frames
 -          = Ff + Fd - Ff frames
 -          = Fd frames
 -          = Fd/f seconds
 - 
 -         So if we accept a difference of 1 frame, ie 1/f seconds, we can
 -         say that
 -
 -         1/f = Fd/f
 -      ie 1 = Fd
 -      ie d = 1/F
 - 
 -         So for a 3hr film, ie F = 3 * 60 * 60 = 10800, the acceptable
 -         FPS error is 1/F ~= 0.0001 ~= 10-e4
 -      */
 -
 -      return (fabs (a - b) < 1e-4);
 -}
 -
  /** @param An arbitrary audio frame rate.
   *  @return The appropriate DCP-approved frame rate (48kHz or 96kHz).
   */
@@@ -734,6 -776,17 +734,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)
  {
        return channels[c];
  }
  
 -FrameRateConversion::FrameRateConversion (float source, int dcp)
 -      : skip (false)
 -      , repeat (1)
 -      , change_speed (false)
 -{
 -      if (fabs (source / 2.0 - dcp) < fabs (source - dcp)) {
 -              /* The difference between source and DCP frame rate will be lower
 -                 (i.e. better) if we skip.
 -              */
 -              skip = true;
 -      } else if (fabs (source * 2 - dcp) < fabs (source - dcp)) {
 -              /* The difference between source and DCP frame rate would be better
 -                 if we repeated each frame once; it may be better still if we
 -                 repeated more than once.  Work out the required repeat.
 -              */
 -              repeat = round (dcp / source);
 -      }
 -
 -      change_speed = !about_equal (source * factor(), dcp);
 -
 -      if (!skip && repeat == 1 && !change_speed) {
 -              description = _("Content and DCP have the same rate.\n");
 -      } else {
 -              if (skip) {
 -                      description = _("DCP will use every other frame of the content.\n");
 -              } else if (repeat == 2) {
 -                      description = _("Each content frame will be doubled in the DCP.\n");
 -              } else if (repeat > 2) {
 -                      description = String::compose (_("Each content frame will be repeated %1 more times in the DCP.\n"), repeat - 1);
 -              }
 -
 -              if (change_speed) {
 -                      float const pc = dcp * 100 / (source * factor());
 -                      description += String::compose (_("DCP will run at %1%% of the content speed.\n"), pc);
 -              }
 -      }
 -}
 -
  LocaleGuard::LocaleGuard ()
        : _old (0)
  {
@@@ -785,7 -876,7 +785,7 @@@ valid_image_file (boost::filesystem::pa
  {
        string ext = f.extension().string();
        transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
-       return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp" || ext == ".tga");
+       return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp" || ext == ".tga" || ext == ".dpx");
  }
  
  string
@@@ -803,7 -894,7 +803,7 @@@ tidy_for_filename (string f
        return t;
  }
  
 -shared_ptr<const libdcp::Signer>
 +shared_ptr<const dcp::Signer>
  make_signer ()
  {
        boost::filesystem::path const sd = Config::instance()->signer_chain_directory ();
                if (!boost::filesystem::exists (p)) {
                        boost::filesystem::remove_all (sd);
                        boost::filesystem::create_directories (sd);
 -                      libdcp::make_signer_chain (sd, openssl_path ());
 +                      dcp::make_signer_chain (sd, openssl_path ());
                        break;
                }
  
                ++i;
        }
        
 -      libdcp::CertificateChain chain;
 +      dcp::CertificateChain chain;
  
        {
                boost::filesystem::path p (sd);
                p /= "ca.self-signed.pem";
 -              chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
 +              chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (p)));
        }
  
        {
                boost::filesystem::path p (sd);
                p /= "intermediate.signed.pem";
 -              chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
 +              chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (p)));
        }
  
        {
                boost::filesystem::path p (sd);
                p /= "leaf.signed.pem";
 -              chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
 +              chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (p)));
        }
  
        boost::filesystem::path signer_key (sd);
        signer_key /= "leaf.key";
  
 -      return shared_ptr<const libdcp::Signer> (new libdcp::Signer (chain, signer_key));
 +      return shared_ptr<const dcp::Signer> (new dcp::Signer (chain, signer_key));
  }
  
  map<string, string>
@@@ -902,14 -993,14 +902,14 @@@ 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)
  {
        if (ratio < full_frame.ratio ()) {
 -              return libdcp::Size (rint (full_frame.height * ratio), full_frame.height);
 +              return dcp::Size (rint (full_frame.height * ratio), full_frame.height);
        }
        
 -      return libdcp::Size (full_frame.width, rint (full_frame.width / ratio));
 +      return dcp::Size (full_frame.width, rint (full_frame.width / ratio));
  }
  
  void *
@@@ -940,24 -1031,6 +940,24 @@@ divide_with_round (int64_t a, int64_t b
        }
  }
  
 +/** Return a user-readable string summarising the versions of our dependencies */
 +string
 +dependency_version_summary ()
 +{
 +      stringstream 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 ();
 +}
 +
  ScopedTemporary::ScopedTemporary ()
        : _open (0)
  {
diff --combined src/wx/film_editor.cc
index 7fa34c516c60875e0208b0f907adf6d332078b94,1131675bc10319216f07ca5a7ed8d549eb84d15b..2cf6a0b6ccc6847a627bfeb63a848a8e407d6ab8
@@@ -803,11 -803,16 +803,16 @@@ FilmEditor::content_add_folder_clicked 
                return;
        }
  
-       _film->examine_and_add_content (
-               shared_ptr<ImageContent> (
-                       new ImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ())))
-                       )
-               );
+       shared_ptr<ImageContent> ic;
+       
+       try {
+               ic.reset (new ImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
+       } catch (FileError& e) {
+               error_dialog (this, std_to_wx (e.what ()));
+               return;
+       }
+       _film->examine_and_add_content (ic);
  }
  
  void
@@@ -849,7 -854,7 +854,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);
  }