Merge master.
authorCarl Hetherington <cth@carlh.net>
Tue, 21 Oct 2014 18:14:58 +0000 (19:14 +0100)
committerCarl Hetherington <cth@carlh.net>
Tue, 21 Oct 2014 18:14:58 +0000 (19:14 +0100)
28 files changed:
1  2 
ChangeLog
cscript
debian/changelog
src/lib/dcp_video.cc
src/lib/image.cc
src/lib/image_examiner.cc
src/lib/player_video.cc
src/lib/player_video.h
src/lib/util.cc
src/tools/dcpomatic_cli.cc
src/tools/po/de_DE.po
src/tools/po/es_ES.po
src/tools/po/fr_FR.po
src/tools/po/it_IT.po
src/tools/po/nl_NL.po
src/wx/about_dialog.cc
src/wx/audio_panel.cc
src/wx/audio_panel.h
src/wx/film_viewer.cc
src/wx/po/de_DE.po
src/wx/po/es_ES.po
src/wx/po/fr_FR.po
src/wx/po/it_IT.po
src/wx/po/nl_NL.po
src/wx/po/sv_SE.po
src/wx/wscript
test/image_test.cc
wscript

diff --cc ChangeLog
Simple merge
diff --cc cscript
Simple merge
Simple merge
index ccfc800c8389f275518b774d3e8e4daa7c1157dd,0000000000000000000000000000000000000000..f6c671fd14774b655acb67708b75084c6a49eaf8
mode 100644,000000..100644
--- /dev/null
@@@ -1,332 -1,0 +1,332 @@@
-               _frame->image (_burn_subtitles),
 +/*
 +    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>
 +#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;
 +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 (AV_PIX_FMT_RGB48LE, _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 ();
 +}
 +
 +/** @return true if this DCPVideo is definitely the same as another;
 + *  (apart from the frame index), false if it is probably not.
 + */
 +bool
 +DCPVideo::same (shared_ptr<const DCPVideo> other) const
 +{
 +      if (_frames_per_second != other->_frames_per_second ||
 +          _j2k_bandwidth != other->_j2k_bandwidth ||
 +          _resolution != other->_resolution ||
 +          _burn_subtitles != other->_burn_subtitles) {
 +              return false;
 +      }
 +
 +      return _frame->same (other->_frame);
 +}
Simple merge
index 004b89e659d47ebd24cb594bc06848d2651585d4,7058ea3b282935c2a9265660500190ae770bd8d2..75ccb6a3e91fd3e21207ce26872f69f187b95170
@@@ -36,10 -36,13 +36,12 @@@ using boost::shared_ptr
  ImageExaminer::ImageExaminer (shared_ptr<const Film> film, shared_ptr<const ImageContent> content, shared_ptr<Job>)
        : _film (film)
        , _image_content (content)
 -      , _video_length (0)
  {
+ #ifdef DCPOMATIC_IMAGE_MAGICK 
        using namespace MagickCore;
+ #endif        
        Magick::Image* image = new Magick::Image (content->path(0).string());
 -      _video_size = libdcp::Size (image->columns(), image->rows());
 +      _video_size = dcp::Size (image->columns(), image->rows());
        delete image;
  
        if (content->still ()) {
index 2feb52f42e9d2ef158a3dc1ad272079958bb1a48,0000000000000000000000000000000000000000..8e6fcd5f3524f8baf4e00c54f791cc92bf4778a7
mode 100644,000000..100644
--- /dev/null
@@@ -1,209 -1,0 +1,209 @@@
- PlayerVideo::image (bool burn_subtitle) const
 +/*
 +    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 +
 +    This program is free software; you can redistribute it and/or modify
 +    it under the terms of the GNU General Public License as published by
 +    the Free Software Foundation; either version 2 of the License, or
 +    (at your option) any later version.
 +
 +    This program is distributed in the hope that it will be useful,
 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +    GNU General Public License for more details.
 +
 +    You should have received a copy of the GNU General Public License
 +    along with this program; if not, write to the Free Software
 +    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 +
 +*/
 +
 +#include <dcp/raw_convert.h>
 +#include "player_video.h"
 +#include "image.h"
 +#include "image_proxy.h"
 +#include "j2k_image_proxy.h"
 +#include "scaler.h"
 +
 +using std::string;
 +using std::cout;
 +using dcp::raw_convert;
 +using boost::shared_ptr;
 +using boost::dynamic_pointer_cast;
 +
 +PlayerVideo::PlayerVideo (
 +      shared_ptr<const ImageProxy> in,
 +      DCPTime time,
 +      Crop crop,
 +      boost::optional<float> fade,
 +      dcp::Size inter_size,
 +      dcp::Size out_size,
 +      Scaler const * scaler,
 +      Eyes eyes,
 +      Part part,
 +      ColourConversion colour_conversion
 +      )
 +      : _in (in)
 +      , _time (time)
 +      , _crop (crop)
 +      , _fade (fade)
 +      , _inter_size (inter_size)
 +      , _out_size (out_size)
 +      , _scaler (scaler)
 +      , _eyes (eyes)
 +      , _part (part)
 +      , _colour_conversion (colour_conversion)
 +{
 +
 +}
 +
 +PlayerVideo::PlayerVideo (shared_ptr<cxml::Node> node, shared_ptr<Socket> socket, shared_ptr<Log> log)
 +{
 +      _time = DCPTime (node->number_child<DCPTime::Type> ("Time"));
 +      _crop = Crop (node);
 +      _fade = node->optional_number_child<float> ("Fade");
 +
 +      _inter_size = dcp::Size (node->number_child<int> ("InterWidth"), node->number_child<int> ("InterHeight"));
 +      _out_size = dcp::Size (node->number_child<int> ("OutWidth"), node->number_child<int> ("OutHeight"));
 +      _scaler = Scaler::from_id (node->string_child ("Scaler"));
 +      _eyes = (Eyes) node->number_child<int> ("Eyes");
 +      _part = (Part) node->number_child<int> ("Part");
 +      _colour_conversion = ColourConversion (node);
 +
 +      _in = image_proxy_factory (node->node_child ("In"), socket, log);
 +
 +      if (node->optional_number_child<int> ("SubtitleX")) {
 +              
 +              _subtitle.position = Position<int> (node->number_child<int> ("SubtitleX"), node->number_child<int> ("SubtitleY"));
 +
 +              _subtitle.image.reset (
 +                      new Image (PIX_FMT_RGBA, dcp::Size (node->number_child<int> ("SubtitleWidth"), node->number_child<int> ("SubtitleHeight")), true)
 +                      );
 +              
 +              _subtitle.image->read_from_socket (socket);
 +      }
 +}
 +
 +void
 +PlayerVideo::set_subtitle (PositionImage image)
 +{
 +      _subtitle = image;
 +}
 +
 +shared_ptr<Image>
-       shared_ptr<Image> out = im->crop_scale_window (total_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, true);
++PlayerVideo::image (AVPixelFormat pixel_format, bool burn_subtitle) const
 +{
 +      shared_ptr<Image> im = _in->image ();
 +      
 +      Crop total_crop = _crop;
 +      switch (_part) {
 +      case PART_LEFT_HALF:
 +              total_crop.right += im->size().width / 2;
 +              break;
 +      case PART_RIGHT_HALF:
 +              total_crop.left += im->size().width / 2;
 +              break;
 +      case PART_TOP_HALF:
 +              total_crop.bottom += im->size().height / 2;
 +              break;
 +      case PART_BOTTOM_HALF:
 +              total_crop.top += im->size().height / 2;
 +              break;
 +      default:
 +              break;
 +      }
 +              
++      shared_ptr<Image> out = im->crop_scale_window (total_crop, _inter_size, _out_size, _scaler, pixel_format, true);
 +
 +      if (burn_subtitle && _subtitle.image) {
 +              out->alpha_blend (_subtitle.image, _subtitle.position);
 +      }
 +
 +      if (_fade) {
 +              out->fade (_fade.get ());
 +      }
 +
 +      return out;
 +}
 +
 +void
 +PlayerVideo::add_metadata (xmlpp::Node* node, bool send_subtitles) const
 +{
 +      node->add_child("Time")->add_child_text (raw_convert<string> (_time.get ()));
 +      _crop.as_xml (node);
 +      if (_fade) {
 +              node->add_child("Fade")->add_child_text (raw_convert<string> (_fade.get ()));
 +      }
 +      _in->add_metadata (node->add_child ("In"));
 +      node->add_child("InterWidth")->add_child_text (raw_convert<string> (_inter_size.width));
 +      node->add_child("InterHeight")->add_child_text (raw_convert<string> (_inter_size.height));
 +      node->add_child("OutWidth")->add_child_text (raw_convert<string> (_out_size.width));
 +      node->add_child("OutHeight")->add_child_text (raw_convert<string> (_out_size.height));
 +      node->add_child("Scaler")->add_child_text (_scaler->id ());
 +      node->add_child("Eyes")->add_child_text (raw_convert<string> (_eyes));
 +      node->add_child("Part")->add_child_text (raw_convert<string> (_part));
 +      _colour_conversion.as_xml (node);
 +      if (send_subtitles && _subtitle.image) {
 +              node->add_child ("SubtitleWidth")->add_child_text (raw_convert<string> (_subtitle.image->size().width));
 +              node->add_child ("SubtitleHeight")->add_child_text (raw_convert<string> (_subtitle.image->size().height));
 +              node->add_child ("SubtitleX")->add_child_text (raw_convert<string> (_subtitle.position.x));
 +              node->add_child ("SubtitleY")->add_child_text (raw_convert<string> (_subtitle.position.y));
 +      }
 +}
 +
 +void
 +PlayerVideo::send_binary (shared_ptr<Socket> socket, bool send_subtitles) const
 +{
 +      _in->send_binary (socket);
 +      if (send_subtitles && _subtitle.image) {
 +              _subtitle.image->write_to_socket (socket);
 +      }
 +}
 +
 +bool
 +PlayerVideo::has_j2k () const
 +{
 +      /* XXX: burnt-in subtitle; maybe other things */
 +      
 +      shared_ptr<const J2KImageProxy> j2k = dynamic_pointer_cast<const J2KImageProxy> (_in);
 +      if (!j2k) {
 +              return false;
 +      }
 +      
 +      return _crop == Crop () && _inter_size == j2k->size();
 +}
 +
 +shared_ptr<EncodedData>
 +PlayerVideo::j2k () const
 +{
 +      shared_ptr<const J2KImageProxy> j2k = dynamic_pointer_cast<const J2KImageProxy> (_in);
 +      assert (j2k);
 +      return j2k->j2k ();
 +}
 +
 +Position<int>
 +PlayerVideo::inter_position () const
 +{
 +      return Position<int> ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.height) / 2);
 +}
 +
 +/** @return true if this PlayerVideo is definitely the same as another
 + * (apart from _time), false if it is probably not
 + */
 +bool
 +PlayerVideo::same (shared_ptr<const PlayerVideo> other) const
 +{
 +      if (_in != other->_in ||
 +          _crop != other->_crop ||
 +          _fade.get_value_or(0) != other->_fade.get_value_or(0) ||
 +          _inter_size != other->_inter_size ||
 +          _out_size != other->_out_size ||
 +          _scaler != other->_scaler ||
 +          _eyes != other->_eyes ||
 +          _part != other->_part ||
 +          _colour_conversion != other->_colour_conversion ||
 +          !_subtitle.same (other->_subtitle)) {
 +              return false;
 +      }
 +
 +      return _in->same (other->_in);
 +}
index 0f5e83b10298ff93c85f0b1dee3ee5dc388d282e,0000000000000000000000000000000000000000..e9d260972824b10e58c3a4a71e2b2388b8bf97a3
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,102 @@@
-       boost::shared_ptr<Image> image (bool burn_subtitle) const;
 +/*
 +    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 +
 +    This program is free software; you can redistribute it and/or modify
 +    it under the terms of the GNU General Public License as published by
 +    the Free Software Foundation; either version 2 of the License, or
 +    (at your option) any later version.
 +
 +    This program is distributed in the hope that it will be useful,
 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +    GNU General Public License for more details.
 +
 +    You should have received a copy of the GNU General Public License
 +    along with this program; if not, write to the Free Software
 +    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 +
 +*/
 +
 +#include <boost/shared_ptr.hpp>
++extern "C" {
++#include <libavutil/pixfmt.h>
++}
 +#include "types.h"
 +#include "position.h"
 +#include "colour_conversion.h"
 +#include "position_image.h"
 +
 +class Image;
 +class ImageProxy;
 +class Scaler;
 +class Socket;
 +class Log;
 +class EncodedData;
 +
 +/** Everything needed to describe a video frame coming out of the player, but with the
 + *  bits still their raw form.  We may want to combine the bits on a remote machine,
 + *  or maybe not even bother to combine them at all.
 + */
 +class PlayerVideo
 +{
 +public:
 +      PlayerVideo (
 +              boost::shared_ptr<const ImageProxy>,
 +              DCPTime,
 +              Crop,
 +              boost::optional<float>,
 +              dcp::Size,
 +              dcp::Size,
 +              Scaler const *,
 +              Eyes,
 +              Part,
 +              ColourConversion
 +              );
 +      
 +      PlayerVideo (boost::shared_ptr<cxml::Node>, boost::shared_ptr<Socket>, boost::shared_ptr<Log>);
 +
 +      void set_subtitle (PositionImage);
 +      
++      boost::shared_ptr<Image> image (AVPixelFormat pix_fmt, bool burn_subtitle) const;
 +
 +      void add_metadata (xmlpp::Node* node, bool send_subtitles) const;
 +      void send_binary (boost::shared_ptr<Socket> socket, bool send_subtitles) const;
 +
 +      bool has_j2k () const;
 +      boost::shared_ptr<EncodedData> j2k () const;
 +
 +      DCPTime time () const {
 +              return _time;
 +      }
 +
 +      Eyes eyes () const {
 +              return _eyes;
 +      }
 +
 +      ColourConversion colour_conversion () const {
 +              return _colour_conversion;
 +      }
 +
 +      /** @return Position of the content within the overall image once it has been scaled up */
 +      Position<int> inter_position () const;
 +
 +      /** @return Size of the content within the overall image once it has been scaled up */
 +      dcp::Size inter_size () const {
 +              return _inter_size;
 +      }
 +
 +      bool same (boost::shared_ptr<const PlayerVideo> other) const;
 +
 +private:
 +      boost::shared_ptr<const ImageProxy> _in;
 +      DCPTime _time;
 +      Crop _crop;
 +      boost::optional<float> _fade;
 +      dcp::Size _inter_size;
 +      dcp::Size _out_size;
 +      Scaler const * _scaler;
 +      Eyes _eyes;
 +      Part _part;
 +      ColourConversion _colour_conversion;
 +      PositionImage _subtitle;
 +};
diff --cc src/lib/util.cc
index e0db5de2ef2aa71558d2bc8a5b3d701e50ee254d,2e9ca66b22ccf597004b6b89892406c0d4d5da3e..7a0f1a17aec65008bf2eea3585d0bebb5cf283c9
  #endif
  #include <glib.h>
  #include <openjpeg.h>
 +#include <pangomm/init.h>
+ #ifdef DCPOMATIC_IMAGE_MAGICK
  #include <magick/MagickCore.h>
+ #else
+ #include <magick/common.h>
+ #include <magick/magick_config.h>
+ #endif
  #include <magick/version.h>
 -#include <libdcp/version.h>
 -#include <libdcp/util.h>
 -#include <libdcp/signer_chain.h>
 -#include <libdcp/signer.h>
 -#include <libdcp/raw_convert.h>
 +#include <dcp/version.h>
 +#include <dcp/util.h>
 +#include <dcp/signer.h>
 +#include <dcp/raw_convert.h>
  extern "C" {
  #include <libavcodec/avcodec.h>
  #include <libavformat/avformat.h>
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 82604763c8e690b8e3c884703617794b2e475b6f,118db7880cbeb42ca05bd4118f246eccd724ac1e..b7d6979d35f20e0988f0b3135c5f0e0acc6fac5f
@@@ -114,9 -114,12 +123,12 @@@ AudioPanel::film_changed (Film::Propert
  {
        switch (property) {
        case Film::AUDIO_CHANNELS:
 -              _mapping->set_channels (_editor->film()->audio_channels ());
 +              _mapping->set_channels (_parent->film()->audio_channels ());
                _sizer->Layout ();
                break;
+       case Film::VIDEO_FRAME_RATE:
+               setup_description ();
+               break;
        default:
                break;
        }
@@@ -136,7 -139,10 +148,9 @@@ AudioPanel::film_content_changed (int p
        if (property == AudioContentProperty::AUDIO_MAPPING) {
                _mapping->set (acs ? acs->audio_mapping () : AudioMapping ());
                _sizer->Layout ();
+       } else if (property == AudioContentProperty::AUDIO_FRAME_RATE) {
+               setup_description ();
        } else if (property == FFmpegContentProperty::AUDIO_STREAM) {
 -              setup_stream_description ();
                _mapping->set (acs ? acs->audio_mapping () : AudioMapping ());
                _sizer->Layout ();
        } else if (property == FFmpegContentProperty::AUDIO_STREAMS) {
@@@ -228,23 -229,56 +242,44 @@@ AudioPanel::stream_changed (
        if (i != a.end ()) {
                fcs->set_audio_stream (*i);
        }
 +}
  
 -      setup_stream_description ();
 +void
 +AudioPanel::processor_changed ()
 +{
 +      string const s = string_client_data (_processor->GetClientObject (_processor->GetSelection ()));
 +      AudioProcessor const * p = 0;
 +      if (s != wx_to_std (N_("none"))) {
 +              p = AudioProcessor::from_id (s);
 +      }
 +              
 +      AudioContentList c = _parent->selected_audio ();
 +      for (AudioContentList::const_iterator i = c.begin(); i != c.end(); ++i) {
 +              (*i)->set_audio_processor (p);
 +      }
  }
  
 -      AudioContentList ac = _editor->selected_audio_content ();
+ void
+ AudioPanel::setup_description ()
+ {
 -      if (acs->content_audio_frame_rate() != acs->output_audio_frame_rate ()) {
++      AudioContentList ac = _parent->selected_audio ();
+       if (ac.size () != 1) {
+               _description->SetLabel ("");
+               return;
+       }
+       shared_ptr<AudioContent> acs = ac.front ();
 -                                              acs->content_audio_frame_rate() / 1000.0,
 -                                              acs->output_audio_frame_rate() / 1000.0
++      if (acs->audio_frame_rate() != acs->resampled_audio_frame_rate ()) {
+               _description->SetLabel (wxString::Format (
+                                               _("Audio will be resampled from %.3fkHz to %.3fkHz."),
 -void
 -AudioPanel::setup_stream_description ()
 -{
 -      FFmpegContentList fc = _editor->selected_ffmpeg_content ();
 -      if (fc.size() != 1) {
 -              _stream_description->SetLabel ("");
 -              return;
 -      }
 -
 -      shared_ptr<FFmpegContent> fcs = fc.front ();
 -
 -      if (!fcs->audio_stream ()) {
 -              _stream_description->SetLabel (wxT (""));
 -      } else {
 -              wxString s;
 -              if (fcs->audio_channels() == 1) {
 -                      s << _("1 channel");
 -              } else {
 -                      s << fcs->audio_channels() << wxT (" ") << _("channels");
 -              }
 -              s << wxT (", ") << fcs->content_audio_frame_rate() << _("Hz");
 -              _stream_description->SetLabel (s);
 -      }
 -}
 -
++                                              acs->audio_frame_rate() / 1000.0,
++                                              acs->resampled_audio_frame_rate() / 1000.0
+                                               ));
+       } else {
+               _description->SetLabel (_("Audio will not be resampled."));
+       }
+ }
  void
  AudioPanel::mapping_changed (AudioMapping m)
  {
@@@ -266,16 -300,12 +301,17 @@@ AudioPanel::content_selection_changed (
        _gain->set_content (sel);
        _delay->set_content (sel);
  
 +      _gain_calculate_button->Enable (sel.size() == 1);
        _show->Enable (sel.size() == 1);
        _stream->Enable (sel.size() == 1);
 +      _processor->Enable (!sel.empty());
        _mapping->Enable (sel.size() == 1);
  
 +      setup_processors ();
 +
        film_content_changed (AudioContentProperty::AUDIO_MAPPING);
 +      film_content_changed (AudioContentProperty::AUDIO_PROCESSOR);
+       film_content_changed (AudioContentProperty::AUDIO_FRAME_RATE);
        film_content_changed (FFmpegContentProperty::AUDIO_STREAM);
        film_content_changed (FFmpegContentProperty::AUDIO_STREAMS);
  }
index d5821d26abd3446844bff8e8aae1b3022901f187,fa18415bdb345fccc09e78a785077e2fa2ffe382..b0218575a7f0f12ca6c5a6b59f3107626e79dbe7
@@@ -42,15 -42,16 +42,18 @@@ private
        void show_clicked ();
        void stream_changed ();
        void mapping_changed (AudioMapping);
 -      void setup_stream_description ();
 +      void processor_changed ();
 +      void setup_processors ();
+       void setup_description ();
  
        ContentSpinCtrlDouble<AudioContent>* _gain;
        wxButton* _gain_calculate_button;
        wxButton* _show;
        ContentSpinCtrl<AudioContent>* _delay;
        wxChoice* _stream;
 +      wxChoice* _processor;
+       wxStaticText* _stream_description;
        AudioMappingView* _mapping;
+       wxStaticText* _description;
        AudioDialog* _audio_dialog;
  };
index 7ecba1903799d28ae658a7eee917bbce6d624983,54cd3e77dc30014816f35b90457d0014ea045f16..a46983a6f9972b0bf41efbc28ef1a4d03372988e
@@@ -169,37 -151,17 +169,37 @@@ FilmViewer::get (DCPTime p, bool accura
                return;
        }
  
 -      /* We could do this with a seek and a fetch_next_frame, but this is
 -         a shortcut to make it quicker.
 -      */
 -
 -      _got_frame = false;
 -      if (!_player->repeat_last_video ()) {
 -              fetch_next_frame ();
 +      list<shared_ptr<PlayerVideo> > pvf = _player->get_video (p, accurate);
 +      if (!pvf.empty ()) {
 +              try {
-                       _frame = pvf.front()->image (true);
++                      _frame = pvf.front()->image (PIX_FMT_RGB24, true);
 +                      _frame = _frame->scale (_frame->size(), Scaler::from_id ("fastbilinear"), PIX_FMT_RGB24, false);
 +                      _position = pvf.front()->time ();
 +                      _inter_position = pvf.front()->inter_position ();
 +                      _inter_size = pvf.front()->inter_size ();
 +              } catch (dcp::DCPReadError& e) {
 +                      /* This can happen on the following sequence of events:
 +                       * - load encrypted DCP
 +                       * - add KDM
 +                       * - DCP is examined again, which sets its "playable" flag to 1
 +                       * - as a side effect of the exam, the viewer is updated using the old pieces
 +                       * - the DCPDecoder in the old piece gives us an encrypted frame
 +                       * - then, the pieces are re-made (but too late).
 +                       *
 +                       * I hope there's a better way to handle this ...
 +                       */
 +                      _frame.reset ();
 +                      _position = p;
 +              }
 +      } else {
 +              _frame.reset ();
 +              _position = p;
        }
 -      
 -      _panel->Refresh ();
 -      _panel->Update ();
 +
 +      set_position_text ();
 +      refresh_panel ();
 +
 +      _last_get_accurate = accurate;
  }
  
  void
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc src/wx/wscript
Simple merge
Simple merge
diff --cc wscript
index fccf5324532a58a945a964e933d9a6d9be407444,62b47d9a4a9427c15f9ae5a1d78633d800a89555..6b55dbdad09f5aabde6ef3dfaf8f23fcf2145175
+++ b/wscript
@@@ -1,9 -1,11 +1,11 @@@
  import subprocess
  import os
  import sys
+ import distutils
+ import distutils.spawn
  
  APPNAME = 'dcpomatic'
 -VERSION = '1.76.2devel'
 +VERSION = '2.0.14devel'
  
  def options(opt):
      opt.load('compiler_cxx')
@@@ -57,15 -60,10 +60,15 @@@ def dynamic_openjpeg(conf)
      conf.check_cfg(package='libopenjpeg', args='--cflags --libs', atleast_version='1.5.0', uselib_store='OPENJPEG', mandatory=True)
      conf.check_cfg(package='libopenjpeg', args='--cflags --libs', max_version='1.5.2', mandatory=True)
  
 +def static_sub(conf):
 +    conf.check_cfg(package='libsub', atleast_version='0.01.0', args='--cflags', uselib_store='SUB', mandatory=True)
 +    conf.env.DEFINES_SUB = [f.replace('\\', '') for f in conf.env.DEFINES_SUB]
 +    conf.env.STLIB_SUB = ['sub']
 +
  def static_dcp(conf, static_boost, static_xmlpp, static_xmlsec, static_ssh):
-     conf.check_cfg(package='libdcp-1.0', atleast_version='0.96', args='--cflags', uselib_store='DCP', mandatory=True)
 -    conf.check_cfg(package='libdcp', atleast_version='0.98', args='--cflags', uselib_store='DCP', mandatory=True)
++    conf.check_cfg(package='libdcp-1.0', atleast_version='1.0', args='--cflags', uselib_store='DCP', mandatory=True)
      conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP]
 -    conf.env.STLIB_DCP = ['dcp', 'asdcp-libdcp', 'kumu-libdcp']
 +    conf.env.STLIB_DCP = ['dcp-1.0', 'asdcp-libdcp-1.0', 'kumu-libdcp-1.0']
      conf.env.LIB_DCP = ['glibmm-2.4', 'ssl', 'crypto', 'bz2', 'xslt']
  
      if static_boost:
@@@ -339,10 -324,14 +342,16 @@@ def configure(conf)
      # Dependencies which are always dynamically linked
      conf.check_cfg(package='sndfile', args='--cflags --libs', uselib_store='SNDFILE', mandatory=True)
      conf.check_cfg(package='glib-2.0', args='--cflags --libs', uselib_store='GLIB', mandatory=True)
-     conf.check_cfg(package= '', path=conf.options.magickpp_config, args='--cppflags --cxxflags --libs', uselib_store='MAGICK', mandatory=True)
+     if distutils.spawn.find_executable(conf.options.magickpp_config):
+         conf.check_cfg(package='', path=conf.options.magickpp_config, args='--cppflags --cxxflags --libs', uselib_store='MAGICK', mandatory=True)
+         conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_IMAGE_MAGICK')
+     else:
+         conf.check_cfg(package='GraphicsMagick++', args='--cflags --libs', uselib_store='MAGICK', mandatory=True)
+         conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_GRAPHICS_MAGICK')
+         
      conf.check_cfg(package='libzip', args='--cflags --libs', uselib_store='ZIP', mandatory=True)
 +    conf.check_cfg(package='pangomm-1.4', args='--cflags --libs', uselib_store='PANGOMM', mandatory=True)
 +    conf.check_cfg(package='cairomm-1.0', args='--cflags --libs', uselib_store='CAIROMM', mandatory=True)
  
      conf.check_cc(fragment="""
                             #include <glib.h>