Merge master.
authorCarl Hetherington <cth@carlh.net>
Sat, 23 Aug 2014 21:50:40 +0000 (22:50 +0100)
committerCarl Hetherington <cth@carlh.net>
Sat, 23 Aug 2014 21:50:40 +0000 (22:50 +0100)
30 files changed:
1  2 
ChangeLog
debian/changelog
src/lib/analyse_audio_job.cc
src/lib/config.cc
src/lib/content.cc
src/lib/dcp_video.cc
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_examiner.cc
src/lib/film.cc
src/lib/filter_graph.cc
src/lib/image_content.cc
src/lib/job.cc
src/lib/kdm.cc
src/lib/server.cc
src/lib/server_finder.cc
src/lib/sndfile_content.cc
src/lib/transcode_job.cc
src/lib/update.cc
src/lib/util.cc
src/lib/video_content.cc
src/lib/wscript
src/tools/dcpomatic.cc
src/tools/dcpomatic_kdm.cc
src/tools/dcpomatic_server_cli.cc
src/wx/about_dialog.cc
src/wx/film_editor.cc
src/wx/properties_dialog.cc
src/wx/timing_panel.cc
test/film_metadata_test.cc

diff --cc ChangeLog
index 23ccae1902d6f8189f305fa33f221f08f27353ae,8b0a5cd3e990b7cdcdc2410db8cfedadc78cd98b..e100fd23e466e0c395a50fe5168e4ada053915d3
+++ b/ChangeLog
@@@ -1,21 -1,24 +1,43 @@@
 +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.
 +
+ 2014-08-23  Carl Hetherington  <cth@carlh.net>
+       * Version 1.72.12 released.
+ 2014-08-23  Carl Hetherington  <cth@carlh.net>
+       * Revert previous use of AVFormatContext::start_time when
+       computing the length of video.  I think this is wrong, and
+       causes bits to be missed off the end of videos (and other
+       problems).
+ 2014-08-20  Carl Hetherington  <cth@carlh.net>
+       * Version 1.72.11 released.
+ 2014-08-19  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>
  
        * Version 1.72.10 released.
index 1da8a1f91707c709a48beaa08630d3c38924a409,f5d36b9dfd9555b521d03aecc23b92620e702b61..09d40178298a9256211e2b1504703a01fb37445c
@@@ -171,15 -171,12 +171,8 @@@ dcpomatic (2.0.1-1) UNRELEASED; urgency
    * New upstream release.
    * New upstream release.
    * New upstream release.
- <<<<<<< HEAD
 -  * New upstream release.
 -  * New upstream release.
 -  * New upstream release.
 -  * New upstream release.
  
 - -- Carl Hetherington <carl@d1stkfactory>  Sat, 23 Aug 2014 18:47:52 +0100
 + -- Carl Hetherington <carl@dalglish>  Wed, 06 Aug 2014 18:59:35 +0100
- =======
-   * New upstream release.
-   * New upstream release.
-  -- Carl Hetherington <carl@d1stkfactory>  Sat, 09 Aug 2014 12:38:18 +0100
- >>>>>>> origin/master
  
  dcpomatic (0.87-1) UNRELEASED; urgency=low
  
Simple merge
Simple merge
index bbbe9b6ce4bdd4dd5ff6b8dcf231c8d08126e253,11a4b21cca3026706911cb0c51ea9230eabc8797..21e49a2c955ae7e74091ec5c1a43f4e9e238b857
@@@ -225,12 -230,12 +225,12 @@@ Content::length_after_trim () cons
  string
  Content::identifier () const
  {
-       stringstream s;
+       SafeStringStream s;
        
        s << Content::digest()
 -        << "_" << position()
 -        << "_" << trim_start()
 -        << "_" << trim_end();
 +        << "_" << position().get()
 +        << "_" << trim_start().get()
 +        << "_" << trim_end().get();
  
        return s.str ();
  }
index 9b1c8c33eae12cf36d214cbd81b84af99e9e7ae8,0000000000000000000000000000000000000000..d849866512d4cdc0e983ca42201631fe3fe88d70
mode 100644,000000..100644
--- /dev/null
@@@ -1,320 -1,0 +1,317 @@@
- #include <sstream>
 +/*
 +    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 +    Taken from code Copyright (C) 2010-2011 Terrence Meiczinger
 +
 +    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.
 +
 +*/
 +
 +/** @file  src/dcp_video_frame.cc
 + *  @brief A single frame of video destined for a DCP.
 + *
 + *  Given an Image and some settings, this class knows how to encode
 + *  the image to J2K either on the local host or on a remote server.
 + *
 + *  Objects of this class are used for the queue that we keep
 + *  of images that require encoding.
 + */
 +
 +#include <stdint.h>
 +#include <cstring>
 +#include <cstdlib>
 +#include <stdexcept>
 +#include <cstdio>
 +#include <iomanip>
- using std::stringstream;
 +#include <iostream>
 +#include <fstream>
 +#include <unistd.h>
 +#include <errno.h>
 +#include <boost/array.hpp>
 +#include <boost/asio.hpp>
 +#include <boost/filesystem.hpp>
 +#include <boost/lexical_cast.hpp>
 +#include <dcp/gamma_lut.h>
 +#include <dcp/xyz_frame.h>
 +#include <dcp/rgb_xyz.h>
 +#include <dcp/colour_matrix.h>
 +#include <dcp/raw_convert.h>
 +#include <libcxml/cxml.h>
 +#include "film.h"
 +#include "dcp_video.h"
 +#include "config.h"
 +#include "exceptions.h"
 +#include "server.h"
 +#include "util.h"
 +#include "scaler.h"
 +#include "image.h"
 +#include "log.h"
 +#include "cross.h"
 +#include "player_video.h"
 +#include "encoded_data.h"
 +
 +#define LOG_GENERAL(...) _log->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
 +
 +#include "i18n.h"
 +
 +using std::string;
-       stringstream xml;
-       doc.write_to_stream (xml, "UTF-8");
-       socket->write (xml.str().length() + 1);
-       socket->write ((uint8_t *) xml.str().c_str(), xml.str().length() + 1);
 +using std::cout;
 +using boost::shared_ptr;
 +using boost::lexical_cast;
 +using dcp::Size;
 +using dcp::raw_convert;
 +
 +#define DCI_COEFFICENT (48.0 / 52.37)
 +
 +/** Construct a DCP video frame.
 + *  @param frame Input frame.
 + *  @param index Index of the frame within the DCP.
 + *  @param bw J2K bandwidth to use (see Config::j2k_bandwidth ())
 + *  @param l Log to write to.
 + */
 +DCPVideo::DCPVideo (
 +      shared_ptr<const PlayerVideo> frame, int index, int dcp_fps, int bw, Resolution r, bool b, shared_ptr<Log> l
 +      )
 +      : _frame (frame)
 +      , _index (index)
 +      , _frames_per_second (dcp_fps)
 +      , _j2k_bandwidth (bw)
 +      , _resolution (r)
 +      , _burn_subtitles (b)
 +      , _log (l)
 +{
 +      
 +}
 +
 +DCPVideo::DCPVideo (shared_ptr<const PlayerVideo> frame, shared_ptr<const cxml::Node> node, shared_ptr<Log> log)
 +      : _frame (frame)
 +      , _log (log)
 +{
 +      _index = node->number_child<int> ("Index");
 +      _frames_per_second = node->number_child<int> ("FramesPerSecond");
 +      _j2k_bandwidth = node->number_child<int> ("J2KBandwidth");
 +      _resolution = Resolution (node->optional_number_child<int>("Resolution").get_value_or (RESOLUTION_2K));
 +      _burn_subtitles = node->bool_child ("BurnSubtitles");
 +}
 +
 +/** J2K-encode this frame on the local host.
 + *  @return Encoded data.
 + */
 +shared_ptr<EncodedData>
 +DCPVideo::encode_locally ()
 +{
 +      shared_ptr<dcp::GammaLUT> in_lut = dcp::GammaLUT::cache.get (
 +              12, _frame->colour_conversion().input_gamma, _frame->colour_conversion().input_gamma_linearised
 +              );
 +      
 +      /* XXX: libdcp should probably use boost */
 +      
 +      double matrix[3][3];
 +      for (int i = 0; i < 3; ++i) {
 +              for (int j = 0; j < 3; ++j) {
 +                      matrix[i][j] = _frame->colour_conversion().matrix (i, j);
 +              }
 +      }
 +
 +      shared_ptr<dcp::XYZFrame> xyz = dcp::rgb_to_xyz (
 +              _frame->image (_burn_subtitles),
 +              in_lut,
 +              dcp::GammaLUT::cache.get (16, 1 / _frame->colour_conversion().output_gamma, false),
 +              matrix
 +              );
 +
 +      /* Set the max image and component sizes based on frame_rate */
 +      int max_cs_len = ((float) _j2k_bandwidth) / 8 / _frames_per_second;
 +      if (_frame->eyes() == EYES_LEFT || _frame->eyes() == EYES_RIGHT) {
 +              /* In 3D we have only half the normal bandwidth per eye */
 +              max_cs_len /= 2;
 +      }
 +      int const max_comp_size = max_cs_len / 1.25;
 +
 +      /* get a J2K compressor handle */
 +      opj_cinfo_t* cinfo = opj_create_compress (CODEC_J2K);
 +      if (cinfo == 0) {
 +              throw EncodeError (N_("could not create JPEG2000 encoder"));
 +      }
 +
 +      /* Set encoding parameters to default values */
 +      opj_cparameters_t parameters;
 +      opj_set_default_encoder_parameters (&parameters);
 +
 +      /* Set default cinema parameters */
 +      parameters.tile_size_on = false;
 +      parameters.cp_tdx = 1;
 +      parameters.cp_tdy = 1;
 +      
 +      /* Tile part */
 +      parameters.tp_flag = 'C';
 +      parameters.tp_on = 1;
 +      
 +      /* Tile and Image shall be at (0,0) */
 +      parameters.cp_tx0 = 0;
 +      parameters.cp_ty0 = 0;
 +      parameters.image_offset_x0 = 0;
 +      parameters.image_offset_y0 = 0;
 +
 +      /* Codeblock size = 32x32 */
 +      parameters.cblockw_init = 32;
 +      parameters.cblockh_init = 32;
 +      parameters.csty |= 0x01;
 +      
 +      /* The progression order shall be CPRL */
 +      parameters.prog_order = CPRL;
 +      
 +      /* No ROI */
 +      parameters.roi_compno = -1;
 +      
 +      parameters.subsampling_dx = 1;
 +      parameters.subsampling_dy = 1;
 +      
 +      /* 9-7 transform */
 +      parameters.irreversible = 1;
 +      
 +      parameters.tcp_rates[0] = 0;
 +      parameters.tcp_numlayers++;
 +      parameters.cp_disto_alloc = 1;
 +      parameters.cp_rsiz = _resolution == RESOLUTION_2K ? CINEMA2K : CINEMA4K;
 +      if (_resolution == RESOLUTION_4K) {
 +              parameters.numpocs = 2;
 +              parameters.POC[0].tile = 1;
 +              parameters.POC[0].resno0 = 0; 
 +              parameters.POC[0].compno0 = 0;
 +              parameters.POC[0].layno1 = 1;
 +              parameters.POC[0].resno1 = parameters.numresolution - 1;
 +              parameters.POC[0].compno1 = 3;
 +              parameters.POC[0].prg1 = CPRL;
 +              parameters.POC[1].tile = 1;
 +              parameters.POC[1].resno0 = parameters.numresolution - 1; 
 +              parameters.POC[1].compno0 = 0;
 +              parameters.POC[1].layno1 = 1;
 +              parameters.POC[1].resno1 = parameters.numresolution;
 +              parameters.POC[1].compno1 = 3;
 +              parameters.POC[1].prg1 = CPRL;
 +      }
 +      
 +      parameters.cp_comment = strdup (N_("DCP-o-matic"));
 +      parameters.cp_cinema = _resolution == RESOLUTION_2K ? CINEMA2K_24 : CINEMA4K_24;
 +
 +      /* 3 components, so use MCT */
 +      parameters.tcp_mct = 1;
 +      
 +      /* set max image */
 +      parameters.max_comp_size = max_comp_size;
 +      parameters.tcp_rates[0] = ((float) (3 * xyz->size().width * xyz->size().height * 12)) / (max_cs_len * 8);
 +
 +      /* Set event manager to null (openjpeg 1.3 bug) */
 +      cinfo->event_mgr = 0;
 +
 +      /* Setup the encoder parameters using the current image and user parameters */
 +      opj_setup_encoder (cinfo, &parameters, xyz->opj_image ());
 +
 +      opj_cio_t* cio = opj_cio_open ((opj_common_ptr) cinfo, 0, 0);
 +      if (cio == 0) {
 +              opj_destroy_compress (cinfo);
 +              throw EncodeError (N_("could not open JPEG2000 stream"));
 +      }
 +
 +      int const r = opj_encode (cinfo, cio, xyz->opj_image(), 0);
 +      if (r == 0) {
 +              opj_cio_close (cio);
 +              opj_destroy_compress (cinfo);
 +              throw EncodeError (N_("JPEG2000 encoding failed"));
 +      }
 +
 +      switch (_frame->eyes()) {
 +      case EYES_BOTH:
 +              LOG_GENERAL (N_("Finished locally-encoded frame %1 for mono"), _index);
 +              break;
 +      case EYES_LEFT:
 +              LOG_GENERAL (N_("Finished locally-encoded frame %1 for L"), _index);
 +              break;
 +      case EYES_RIGHT:
 +              LOG_GENERAL (N_("Finished locally-encoded frame %1 for R"), _index);
 +              break;
 +      default:
 +              break;
 +      }
 +
 +      shared_ptr<EncodedData> enc (new LocallyEncodedData (cio->buffer, cio_tell (cio)));
 +
 +      opj_cio_close (cio);
 +      free (parameters.cp_comment);
 +      opj_destroy_compress (cinfo);
 +
 +      return enc;
 +}
 +
 +/** Send this frame to a remote server for J2K encoding, then read the result.
 + *  @param serv Server to send to.
 + *  @return Encoded data.
 + */
 +shared_ptr<EncodedData>
 +DCPVideo::encode_remotely (ServerDescription serv)
 +{
 +      boost::asio::io_service io_service;
 +      boost::asio::ip::tcp::resolver resolver (io_service);
 +      boost::asio::ip::tcp::resolver::query query (serv.host_name(), raw_convert<string> (Config::instance()->server_port_base ()));
 +      boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query);
 +
 +      shared_ptr<Socket> socket (new Socket);
 +
 +      socket->connect (*endpoint_iterator);
 +
 +      /* Collect all XML metadata */
 +      xmlpp::Document doc;
 +      xmlpp::Element* root = doc.create_root_node ("EncodingRequest");
 +      root->add_child("Version")->add_child_text (raw_convert<string> (SERVER_LINK_VERSION));
 +      add_metadata (root);
 +
 +      LOG_GENERAL (N_("Sending frame %1 to remote"), _index);
 +      
 +      /* Send XML metadata */
++      string xml = doc.write_to_string ("UTF-8");
++      socket->write (xml.length() + 1);
++      socket->write ((uint8_t *) xml.c_str(), xml.length() + 1);
 +
 +      /* Send binary data */
 +      _frame->send_binary (socket, _burn_subtitles);
 +
 +      /* Read the response (JPEG2000-encoded data); this blocks until the data
 +         is ready and sent back.
 +      */
 +      shared_ptr<EncodedData> e (new RemotelyEncodedData (socket->read_uint32 ()));
 +      socket->read (e->data(), e->size());
 +
 +      LOG_GENERAL (N_("Finished remotely-encoded frame %1"), _index);
 +      
 +      return e;
 +}
 +
 +void
 +DCPVideo::add_metadata (xmlpp::Element* el) const
 +{
 +      el->add_child("Index")->add_child_text (raw_convert<string> (_index));
 +      el->add_child("FramesPerSecond")->add_child_text (raw_convert<string> (_frames_per_second));
 +      el->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
 +      el->add_child("Resolution")->add_child_text (raw_convert<string> (int (_resolution)));
 +      el->add_child("BurnSubtitles")->add_child_text (_burn_subtitles ? "1" : "0");
 +      _frame->add_metadata (el, _burn_subtitles);
 +}
 +
 +Eyes
 +DCPVideo::eyes () const
 +{
 +      return _frame->eyes ();
 +}
 +
index 0af53d8830a52ab8ca8518b0bae5591be12c6127,a4209f5b648306e734861c56ea96329b79a25a4a..bb4e022308dc9882dff57d5a0959d0a014cf9cab
@@@ -235,9 -241,9 +235,9 @@@ FFmpegContent::information () cons
                return "";
        }
        
-       stringstream s;
+       SafeStringStream s;
        
 -      s << String::compose (_("%1 frames; %2 frames per second"), video_length_after_3d_combine(), video_frame_rate()) << "\n";
 +      s << String::compose (_("%1 frames; %2 frames per second"), video_length_after_3d_combine().frames (video_frame_rate()), video_frame_rate()) << "\n";
        s << VideoContent::information ();
  
        return s.str ();
Simple merge
index 62b909b1d65f3708293ca8dcbe91fe4a371d4608,5ccc8028b6ebae14dfbf6f1434b94e0c49a7e681..48d85da6f11113cb6cabc83215505c8df766110a
@@@ -23,9 -23,7 +23,10 @@@ extern "C" 
  }
  #include "ffmpeg_examiner.h"
  #include "ffmpeg_content.h"
 +#include "ffmpeg_audio_stream.h"
 +#include "ffmpeg_subtitle_stream.h"
 +#include "util.h"
+ #include "safe_stringstream.h"
  
  #include "i18n.h"
  
@@@ -159,25 -118,27 +159,25 @@@ FFmpegExaminer::frame_time (AVStream* s
  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
  {
-       ContentTime const length = ContentTime::from_seconds (double (_format_context->duration - _format_context->start_time) / AV_TIME_BASE);
 -      VideoContent::Frame const length = (double (_format_context->duration) / AV_TIME_BASE) * video_frame_rate();
 -      return max (1, length);
++      ContentTime const length = ContentTime::from_seconds (double (_format_context->duration) / AV_TIME_BASE);
 +      return ContentTime (max (ContentTime::Type (1), length.get ()));
  }
  
  string
diff --cc src/lib/film.cc
Simple merge
Simple merge
index 70d777bca11f6e52a466dbf44414b7a1ab048163,915da7beba33b70687e0536d751d4e8e6e96a65f..84b0b75c9732a4cd47dc30e1698ee1a82335c675
@@@ -24,7 -25,7 +24,8 @@@
  #include "film.h"
  #include "job.h"
  #include "frame_rate_change.h"
 +#include "exceptions.h"
+ #include "safe_stringstream.h"
  
  #include "i18n.h"
  
@@@ -134,9 -138,9 +134,9 @@@ ImageContent::full_length () cons
  string
  ImageContent::identifier () const
  {
-       stringstream s;
+       SafeStringStream s;
        s << VideoContent::identifier ();
 -      s << "_" << video_length();
 +      s << "_" << video_length().get();
        return s.str ();
  }
  
diff --cc src/lib/job.cc
Simple merge
diff --cc src/lib/kdm.cc
Simple merge
index 5598ef69f8388aae66098669ad9361e369691ab9,9591be1886414dc77f6f335dbb976c0c857bad93..d2c573c1b404c2e37f07269897511be56f327851
  #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"
  
Simple merge
index a573d43c407f8ac8a825190a1784600ba37ac873,ba3bd0a772d489714a01bebd7c90d56ab1b063e6..1a17976657c42759a10566aaec00559de94e1ba9
  #include "i18n.h"
  
  using std::string;
- using std::stringstream;
  using std::cout;
  using boost::shared_ptr;
 -using libdcp::raw_convert;
 +using dcp::raw_convert;
  
  SndfileContent::SndfileContent (shared_ptr<const Film> f, boost::filesystem::path p)
        : Content (f, p)
index 1a162b6544ad1ba6442acbfbdcaab87d52b4bd95,831c74b3b244d9a78b4369eb3d089852c74c9174..23a46d06dddcb7875a9247911d387028a0da802e
  #define LOG_ERROR_NC(...)   _film->log()->log (__VA_ARGS__, Log::TYPE_ERROR);
  
  using std::string;
- using std::stringstream;
  using std::fixed;
  using std::setprecision;
 +using std::cout;
  using boost::shared_ptr;
  
  /** @param s Film to use.
index 44ecbb232c26b03214f23d3191c54fedd9a9071a,7bec061e9292e15d044c360e8c13d60dc6a0faab..0146df48430deff9a410763e6a46bf7894b9af4d
@@@ -109,14 -100,11 +109,13 @@@ UpdateChecker::thread (
                                set_state (FAILED);
                                return;
                        }
 +
 +                      /* Parse the reply */
                        
                        _buffer[_offset] = '\0';
-                       stringstream s;
-                       s << _buffer;
+                       string s (_buffer);
                        cxml::Document doc ("Update");
-                       doc.read_stream (s);
+                       doc.read_string (s);
                        
                        {
                                boost::mutex::scoped_lock lm (_data_mutex);
diff --cc src/lib/util.cc
index 60953b544d1a0c26b3816c674db3f9612b4ee181,d96001d13916fcf3eda519c625ce0d68f15b512a..c9685aa44ff3ee811e1f7e77e052b8047ca8ff1d
@@@ -69,9 -68,8 +68,10 @@@ extern "C" 
  #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"
  #endif
@@@ -885,27 -951,6 +884,27 @@@ divide_with_round (int64_t a, int64_t b
        }
  }
  
-       stringstream s;
 +/** 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)
  {
index 0a3e378eecb1fda2f05ef3b6894659c6849386fe,b0d4c2de506cb2b4abd828639365b8d786342354..796e6575a2e62f2088affaafea41a7240adb866b
@@@ -31,7 -31,7 +31,8 @@@
  #include "film.h"
  #include "exceptions.h"
  #include "frame_rate_change.h"
 +#include "log.h"
+ #include "safe_stringstream.h"
  
  #include "i18n.h"
  
diff --cc src/lib/wscript
index 15f26c34f2dcc91cfeef95d19a142301eebd5e38,1e4efddc4fbb2e49986265a2a877f44344ecd930..2510ebe2a481b75d4d8b0c90f3916420f43350f7
@@@ -55,16 -41,14 +55,17 @@@ sources = ""
            job_manager.cc
            kdm.cc
            log.cc
 +          magick_image_proxy.cc
            md5_digester.cc
 -          piece.cc
 +          mid_side_decoder.cc
            player.cc
 -          player_video_frame.cc
 +          player_video.cc
            playlist.cc
            ratio.cc
 +          raw_image_proxy.cc
 +          render_subtitles.cc
            resampler.cc
+           safe_stringstream.cc
            scp_dcp_job.cc
            scaler.cc
            send_kdm_email_job.cc
Simple merge
Simple merge
Simple merge
Simple merge
index 37a8c880847391eb68d29bdbe7063c5940c4a7cc,87310d21ae598ac93d1b67a8e40d3b2e8c61082c..1ce4695d6bf83c2e00c09df358667e5551374da1
@@@ -44,7 -45,7 +44,8 @@@
  #include "lib/playlist.h"
  #include "lib/content.h"
  #include "lib/content_factory.h"
 +#include "lib/dcp_content.h"
+ #include "lib/safe_stringstream.h"
  #include "timecode.h"
  #include "wx_util.h"
  #include "film_editor.h"
@@@ -107,9 -420,96 +107,8 @@@ FilmEditor::film_changed (Film::Propert
                return;
        }
  
 -      SafeStringStream s;
 -
 -      for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
 -              (*i)->film_changed (p);
 -      }
 -              
 -      switch (p) {
 -      case Film::NONE:
 -              break;
 -      case Film::CONTENT:
 -              setup_content ();
 -              break;
 -      case Film::CONTAINER:
 -              setup_container ();
 -              break;
 -      case Film::NAME:
 -              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 ();
 -              break;
 -      case Film::SCALER:
 -              checked_set (_scaler, Scaler::as_index (_film->scaler ()));
 -              break;
 -      case Film::SIGNED:
 -              checked_set (_signed, _film->is_signed ());
 -              break;
 -      case Film::ENCRYPTED:
 -              checked_set (_encrypted, _film->encrypted ());
 -              if (_film->encrypted ()) {
 -                      _film->set_signed (true);
 -                      _signed->Enable (false);
 -              } else {
 -                      _signed->Enable (_generally_sensitive);
 -              }
 -              break;
 -      case Film::RESOLUTION:
 -              checked_set (_resolution, _film->resolution() == RESOLUTION_2K ? 0 : 1);
 -              setup_dcp_name ();
 -              break;
 -      case Film::J2K_BANDWIDTH:
 -              checked_set (_j2k_bandwidth, _film->j2k_bandwidth() / 1000000);
 -              break;
 -      case Film::USE_ISDCF_NAME:
 -              checked_set (_use_isdcf_name, _film->use_isdcf_name ());
 -              setup_dcp_name ();
 -              break;
 -      case Film::ISDCF_METADATA:
 -              setup_dcp_name ();
 -              break;
 -      case Film::VIDEO_FRAME_RATE:
 -      {
 -              bool done = false;
 -              for (unsigned int i = 0; i < _frame_rate_choice->GetCount(); ++i) {
 -                      if (wx_to_std (_frame_rate_choice->GetString(i)) == boost::lexical_cast<string> (_film->video_frame_rate())) {
 -                              checked_set (_frame_rate_choice, i);
 -                              done = true;
 -                              break;
 -                      }
 -              }
 -
 -              if (!done) {
 -                      checked_set (_frame_rate_choice, -1);
 -              }
 -
 -              _frame_rate_spin->SetValue (_film->video_frame_rate ());
 -
 -              _best_frame_rate->Enable (_film->best_video_frame_rate () != _film->video_frame_rate ());
 -              break;
 -      }
 -      case Film::AUDIO_CHANNELS:
 -              checked_set (_audio_channels, _film->audio_channels ());
 -              setup_dcp_name ();
 -              break;
 -      case Film::SEQUENCE_VIDEO:
 -              checked_set (_sequence_video, _film->sequence_video ());
 -              break;
 -      case Film::THREE_D:
 -              checked_set (_three_d, _film->three_d ());
 -              setup_dcp_name ();
 -              break;
 -      case Film::INTEROP:
 -              checked_set (_standard, _film->interop() ? 1 : 0);
 -              break;
 -      }
 +      _content_panel->film_changed (p);
 +      _dcp_panel->film_changed (p);
  }
  
  void
@@@ -171,4 -720,343 +170,3 @@@ FilmEditor::active_jobs_changed (bool a
  {
        set_general_sensitivity (!a);
  }
 -
 -void
 -FilmEditor::setup_dcp_name ()
 -{
 -      string s = _film->dcp_name (true);
 -      if (s.length() > 28) {
 -              _dcp_name->SetLabel (std_to_wx (s.substr (0, 28)) + N_("..."));
 -              _dcp_name->SetToolTip (std_to_wx (s));
 -      } else {
 -              _dcp_name->SetLabel (std_to_wx (s));
 -      }
 -}
 -
 -void
 -FilmEditor::best_frame_rate_clicked ()
 -{
 -      if (!_film) {
 -              return;
 -      }
 -      
 -      _film->set_video_frame_rate (_film->best_video_frame_rate ());
 -}
 -
 -void
 -FilmEditor::setup_content ()
 -{
 -      string selected_summary;
 -      int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
 -      if (s != -1) {
 -              selected_summary = wx_to_std (_content->GetItemText (s));
 -      }
 -      
 -      _content->DeleteAllItems ();
 -
 -      ContentList content = _film->content ();
 -      sort (content.begin(), content.end(), ContentSorter ());
 -      
 -      for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
 -              int const t = _content->GetItemCount ();
 -              bool const valid = (*i)->paths_valid ();
 -
 -              string s = (*i)->summary ();
 -              if (!valid) {
 -                      s = _("MISSING: ") + s;
 -              }
 -
 -              _content->InsertItem (t, std_to_wx (s));
 -
 -              if ((*i)->summary() == selected_summary) {
 -                      _content->SetItemState (t, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
 -              }
 -
 -              if (!valid) {
 -                      _content->SetItemTextColour (t, *wxRED);
 -              }
 -      }
 -
 -      if (selected_summary.empty () && !content.empty ()) {
 -              /* Select the item of content if none was selected before */
 -              _content->SetItemState (0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
 -      }
 -}
 -
 -void
 -FilmEditor::content_add_file_clicked ()
 -{
 -      /* The wxFD_CHANGE_DIR here prevents a `could not set working directory' error 123 on Windows when using
 -         non-Latin filenames or paths.
 -      */
 -      wxFileDialog* d = new wxFileDialog (this, _("Choose a file or files"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE | wxFD_CHANGE_DIR);
 -      int const r = d->ShowModal ();
 -
 -      if (r != wxID_OK) {
 -              d->Destroy ();
 -              return;
 -      }
 -
 -      wxArrayString paths;
 -      d->GetPaths (paths);
 -
 -      /* XXX: check for lots of files here and do something */
 -
 -      for (unsigned int i = 0; i < paths.GetCount(); ++i) {
 -              _film->examine_and_add_content (content_factory (_film, wx_to_std (paths[i])));
 -      }
 -
 -      d->Destroy ();
 -}
 -
 -void
 -FilmEditor::content_add_folder_clicked ()
 -{
 -      wxDirDialog* d = new wxDirDialog (this, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST);
 -      int const r = d->ShowModal ();
 -      d->Destroy ();
 -      
 -      if (r != wxID_OK) {
 -              return;
 -      }
 -
 -      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
 -FilmEditor::content_remove_clicked ()
 -{
 -      ContentList c = selected_content ();
 -      for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
 -              _film->remove_content (*i);
 -      }
 -
 -      content_selection_changed ();
 -}
 -
 -void
 -FilmEditor::content_selection_changed ()
 -{
 -      setup_content_sensitivity ();
 -
 -      for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
 -              (*i)->content_selection_changed ();
 -      }
 -}
 -
 -/** Set up broad sensitivity based on the type of content that is selected */
 -void
 -FilmEditor::setup_content_sensitivity ()
 -{
 -      _content_add_file->Enable (_generally_sensitive);
 -      _content_add_folder->Enable (_generally_sensitive);
 -
 -      ContentList selection = selected_content ();
 -      VideoContentList video_selection = selected_video_content ();
 -      AudioContentList audio_selection = selected_audio_content ();
 -
 -      _content_remove->Enable   (!selection.empty() && _generally_sensitive);
 -      _content_earlier->Enable  (selection.size() == 1 && _generally_sensitive);
 -      _content_later->Enable    (selection.size() == 1 && _generally_sensitive);
 -      _content_timeline->Enable (!_film->content().empty() && _generally_sensitive);
 -
 -      _video_panel->Enable    (!video_selection.empty() && _generally_sensitive);
 -      _audio_panel->Enable    (!audio_selection.empty() && _generally_sensitive);
 -      _subtitle_panel->Enable (selection.size() == 1 && dynamic_pointer_cast<FFmpegContent> (selection.front()) && _generally_sensitive);
 -      _timing_panel->Enable   (!selection.empty() && _generally_sensitive);
 -}
 -
 -ContentList
 -FilmEditor::selected_content ()
 -{
 -      ContentList sel;
 -
 -      if (!_film) {
 -              return sel;
 -      }
 -
 -      /* The list was populated using a sorted content list, so we must sort it here too
 -         so that we can look up by index and get the right thing.
 -      */
 -      ContentList content = _film->content ();
 -      sort (content.begin(), content.end(), ContentSorter ());
 -      
 -      long int s = -1;
 -      while (true) {
 -              s = _content->GetNextItem (s, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
 -              if (s == -1) {
 -                      break;
 -              }
 -
 -              if (s < int (_film->content().size ())) {
 -                      sel.push_back (content[s]);
 -              }
 -      }
 -
 -      return sel;
 -}
 -
 -VideoContentList
 -FilmEditor::selected_video_content ()
 -{
 -      ContentList c = selected_content ();
 -      VideoContentList vc;
 -      
 -      for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
 -              shared_ptr<VideoContent> t = dynamic_pointer_cast<VideoContent> (*i);
 -              if (t) {
 -                      vc.push_back (t);
 -              }
 -      }
 -
 -      return vc;
 -}
 -
 -AudioContentList
 -FilmEditor::selected_audio_content ()
 -{
 -      ContentList c = selected_content ();
 -      AudioContentList ac;
 -      
 -      for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
 -              shared_ptr<AudioContent> t = dynamic_pointer_cast<AudioContent> (*i);
 -              if (t) {
 -                      ac.push_back (t);
 -              }
 -      }
 -
 -      return ac;
 -}
 -
 -SubtitleContentList
 -FilmEditor::selected_subtitle_content ()
 -{
 -      ContentList c = selected_content ();
 -      SubtitleContentList sc;
 -      
 -      for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
 -              shared_ptr<SubtitleContent> t = dynamic_pointer_cast<SubtitleContent> (*i);
 -              if (t) {
 -                      sc.push_back (t);
 -              }
 -      }
 -
 -      return sc;
 -}
 -
 -FFmpegContentList
 -FilmEditor::selected_ffmpeg_content ()
 -{
 -      ContentList c = selected_content ();
 -      FFmpegContentList sc;
 -      
 -      for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
 -              shared_ptr<FFmpegContent> t = dynamic_pointer_cast<FFmpegContent> (*i);
 -              if (t) {
 -                      sc.push_back (t);
 -              }
 -      }
 -
 -      return sc;
 -}
 -
 -void
 -FilmEditor::content_timeline_clicked ()
 -{
 -      if (_timeline_dialog) {
 -              _timeline_dialog->Destroy ();
 -              _timeline_dialog = 0;
 -      }
 -      
 -      _timeline_dialog = new TimelineDialog (this, _film);
 -      _timeline_dialog->Show ();
 -}
 -
 -void
 -FilmEditor::set_selection (weak_ptr<Content> wc)
 -{
 -      ContentList content = _film->content ();
 -      for (size_t i = 0; i < content.size(); ++i) {
 -              if (content[i] == wc.lock ()) {
 -                      _content->SetItemState (i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
 -              } else {
 -                      _content->SetItemState (i, 0, wxLIST_STATE_SELECTED);
 -              }
 -      }
 -}
 -
 -void
 -FilmEditor::sequence_video_changed ()
 -{
 -      if (!_film) {
 -              return;
 -      }
 -      
 -      _film->set_sequence_video (_sequence_video->GetValue ());
 -}
 -
 -void
 -FilmEditor::content_right_click (wxListEvent& ev)
 -{
 -      _menu.popup (_film, selected_content (), ev.GetPoint ());
 -}
 -
 -void
 -FilmEditor::three_d_changed ()
 -{
 -      if (!_film) {
 -              return;
 -      }
 -
 -      _film->set_three_d (_three_d->GetValue ());
 -}
 -
 -void
 -FilmEditor::content_earlier_clicked ()
 -{
 -      ContentList sel = selected_content ();
 -      if (sel.size() == 1) {
 -              _film->move_content_earlier (sel.front ());
 -              content_selection_changed ();
 -      }
 -}
 -
 -void
 -FilmEditor::content_later_clicked ()
 -{
 -      ContentList sel = selected_content ();
 -      if (sel.size() == 1) {
 -              _film->move_content_later (sel.front ());
 -              content_selection_changed ();
 -      }
 -}
 -
 -void
 -FilmEditor::config_changed ()
 -{
 -      _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
 -      setup_frame_rate_widget ();
 -}
 -
 -void
 -FilmEditor::setup_frame_rate_widget ()
 -{
 -      if (Config::instance()->allow_any_dcp_frame_rate ()) {
 -              _frame_rate_choice->Hide ();
 -              _frame_rate_spin->Show ();
 -      } else {
 -              _frame_rate_choice->Show ();
 -              _frame_rate_spin->Hide ();
 -      }
--
 -      _frame_rate_sizer->Layout ();
 -}
index 801996efa7bcb611111285a847c41848591f69b7,53ca237555af31672f2dd2517038dc829ce270bf..27fc75b1b39c649fd76b9890d1758e374f7af858
@@@ -45,9 -45,10 +45,9 @@@ PropertiesDialog::PropertiesDialog (wxW
        add (_("Frames already encoded"), true);
        _encoded = add (new ThreadedStaticText (this, _("counting..."), boost::bind (&PropertiesDialog::frames_already_encoded, this)));
        _encoded->Finished.connect (boost::bind (&PropertiesDialog::layout, this));
 -      
 -      _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->time_to_video_frames (_film->length()))));
 +      _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->length().frames (_film->video_frame_rate ()))));
        double const disk = double (_film->required_disk_space()) / 1073741824.0f;
-       stringstream s;
+       SafeStringStream s;
        s << fixed << setprecision (1) << disk << wx_to_std (_("Gb"));
        _disk->SetLabel (std_to_wx (s.str ()));
  
Simple merge
index f0a544e50a9f57ed178fb4705696fb14ca810d9f,c9f4a2c38faaab5b4d82bbb0ece8a550b0fc3537..01cff5b06c46fbb993eb397f68bdefc9a1329a0e
  
  */
  
- #include <sstream>
 +/** @file  test/film_metadata_test.cc
 + *  @brief Test some basic reading/writing of film metadata.
 + */
 +
  #include <boost/test/unit_test.hpp>
  #include <boost/filesystem.hpp>
  #include <boost/date_time.hpp>