--- /dev/null
- _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 (¶meters);
+
+ /* 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, ¶meters, 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);
+}
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 ()) {
--- /dev/null
- 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);
+}
--- /dev/null
- 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;
+};
#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>
{
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;
}
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) {
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)
{
_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);
}
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;
};
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
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')
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:
# 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>