From: Carl Hetherington Date: Tue, 21 Oct 2014 18:14:58 +0000 (+0100) Subject: Merge master. X-Git-Tag: v2.0.48~551 X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=cb990adba9c57e5107ef2aa9716cf0a26c1df83d Merge master. --- cb990adba9c57e5107ef2aa9716cf0a26c1df83d diff --cc src/lib/dcp_video.cc index ccfc800c8,000000000..f6c671fd1 mode 100644,000000..100644 --- a/src/lib/dcp_video.cc +++ b/src/lib/dcp_video.cc @@@ -1,332 -1,0 +1,332 @@@ +/* + Copyright (C) 2012-2014 Carl Hetherington + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 frame, int index, int dcp_fps, int bw, Resolution r, bool b, shared_ptr l + ) + : _frame (frame) + , _index (index) + , _frames_per_second (dcp_fps) + , _j2k_bandwidth (bw) + , _resolution (r) + , _burn_subtitles (b) + , _log (l) +{ + +} + +DCPVideo::DCPVideo (shared_ptr frame, shared_ptr node, shared_ptr log) + : _frame (frame) + , _log (log) +{ + _index = node->number_child ("Index"); + _frames_per_second = node->number_child ("FramesPerSecond"); + _j2k_bandwidth = node->number_child ("J2KBandwidth"); + _resolution = Resolution (node->optional_number_child("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 +DCPVideo::encode_locally () +{ + shared_ptr 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 xyz = dcp::rgb_to_xyz ( - _frame->image (_burn_subtitles), ++ _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 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 +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 (Config::instance()->server_port_base ())); + boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query); + + shared_ptr 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 (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 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 (_index)); + el->add_child("FramesPerSecond")->add_child_text (raw_convert (_frames_per_second)); + el->add_child("J2KBandwidth")->add_child_text (raw_convert (_j2k_bandwidth)); + el->add_child("Resolution")->add_child_text (raw_convert (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 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); +} diff --cc src/lib/image_examiner.cc index 004b89e65,7058ea3b2..75ccb6a3e --- a/src/lib/image_examiner.cc +++ b/src/lib/image_examiner.cc @@@ -36,10 -36,13 +36,12 @@@ using boost::shared_ptr ImageExaminer::ImageExaminer (shared_ptr film, shared_ptr content, shared_ptr) : _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 ()) { diff --cc src/lib/player_video.cc index 2feb52f42,000000000..8e6fcd5f3 mode 100644,000000..100644 --- a/src/lib/player_video.cc +++ b/src/lib/player_video.cc @@@ -1,209 -1,0 +1,209 @@@ +/* + Copyright (C) 2013-2014 Carl Hetherington + + 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 +#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 in, + DCPTime time, + Crop crop, + boost::optional 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 node, shared_ptr socket, shared_ptr log) +{ + _time = DCPTime (node->number_child ("Time")); + _crop = Crop (node); + _fade = node->optional_number_child ("Fade"); + + _inter_size = dcp::Size (node->number_child ("InterWidth"), node->number_child ("InterHeight")); + _out_size = dcp::Size (node->number_child ("OutWidth"), node->number_child ("OutHeight")); + _scaler = Scaler::from_id (node->string_child ("Scaler")); + _eyes = (Eyes) node->number_child ("Eyes"); + _part = (Part) node->number_child ("Part"); + _colour_conversion = ColourConversion (node); + + _in = image_proxy_factory (node->node_child ("In"), socket, log); + + if (node->optional_number_child ("SubtitleX")) { + + _subtitle.position = Position (node->number_child ("SubtitleX"), node->number_child ("SubtitleY")); + + _subtitle.image.reset ( + new Image (PIX_FMT_RGBA, dcp::Size (node->number_child ("SubtitleWidth"), node->number_child ("SubtitleHeight")), true) + ); + + _subtitle.image->read_from_socket (socket); + } +} + +void +PlayerVideo::set_subtitle (PositionImage image) +{ + _subtitle = image; +} + +shared_ptr - PlayerVideo::image (bool burn_subtitle) const ++PlayerVideo::image (AVPixelFormat pixel_format, bool burn_subtitle) const +{ + shared_ptr 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 out = im->crop_scale_window (total_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, true); ++ shared_ptr 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 (_time.get ())); + _crop.as_xml (node); + if (_fade) { + node->add_child("Fade")->add_child_text (raw_convert (_fade.get ())); + } + _in->add_metadata (node->add_child ("In")); + node->add_child("InterWidth")->add_child_text (raw_convert (_inter_size.width)); + node->add_child("InterHeight")->add_child_text (raw_convert (_inter_size.height)); + node->add_child("OutWidth")->add_child_text (raw_convert (_out_size.width)); + node->add_child("OutHeight")->add_child_text (raw_convert (_out_size.height)); + node->add_child("Scaler")->add_child_text (_scaler->id ()); + node->add_child("Eyes")->add_child_text (raw_convert (_eyes)); + node->add_child("Part")->add_child_text (raw_convert (_part)); + _colour_conversion.as_xml (node); + if (send_subtitles && _subtitle.image) { + node->add_child ("SubtitleWidth")->add_child_text (raw_convert (_subtitle.image->size().width)); + node->add_child ("SubtitleHeight")->add_child_text (raw_convert (_subtitle.image->size().height)); + node->add_child ("SubtitleX")->add_child_text (raw_convert (_subtitle.position.x)); + node->add_child ("SubtitleY")->add_child_text (raw_convert (_subtitle.position.y)); + } +} + +void +PlayerVideo::send_binary (shared_ptr 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 j2k = dynamic_pointer_cast (_in); + if (!j2k) { + return false; + } + + return _crop == Crop () && _inter_size == j2k->size(); +} + +shared_ptr +PlayerVideo::j2k () const +{ + shared_ptr j2k = dynamic_pointer_cast (_in); + assert (j2k); + return j2k->j2k (); +} + +Position +PlayerVideo::inter_position () const +{ + return Position ((_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 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); +} diff --cc src/lib/player_video.h index 0f5e83b10,000000000..e9d260972 mode 100644,000000..100644 --- a/src/lib/player_video.h +++ b/src/lib/player_video.h @@@ -1,99 -1,0 +1,102 @@@ +/* + Copyright (C) 2013-2014 Carl Hetherington + + 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 ++extern "C" { ++#include ++} +#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, + DCPTime, + Crop, + boost::optional, + dcp::Size, + dcp::Size, + Scaler const *, + Eyes, + Part, + ColourConversion + ); + + PlayerVideo (boost::shared_ptr, boost::shared_ptr, boost::shared_ptr); + + void set_subtitle (PositionImage); + - boost::shared_ptr image (bool burn_subtitle) const; ++ boost::shared_ptr 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, bool send_subtitles) const; + + bool has_j2k () const; + boost::shared_ptr 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 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 other) const; + +private: + boost::shared_ptr _in; + DCPTime _time; + Crop _crop; + boost::optional _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 e0db5de2e,2e9ca66b2..7a0f1a17a --- a/src/lib/util.cc +++ b/src/lib/util.cc @@@ -43,13 -43,18 +43,18 @@@ #endif #include #include +#include + #ifdef DCPOMATIC_IMAGE_MAGICK #include + #else + #include + #include + #endif #include -#include -#include -#include -#include -#include +#include +#include +#include +#include extern "C" { #include #include diff --cc src/wx/audio_panel.cc index 82604763c,118db7880..b7d6979d3 --- a/src/wx/audio_panel.cc +++ b/src/wx/audio_panel.cc @@@ -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); + } } + void + AudioPanel::setup_description () + { - AudioContentList ac = _editor->selected_audio_content (); ++ AudioContentList ac = _parent->selected_audio (); + if (ac.size () != 1) { + _description->SetLabel (""); + return; + } + + shared_ptr acs = ac.front (); - if (acs->content_audio_frame_rate() != acs->output_audio_frame_rate ()) { ++ if (acs->audio_frame_rate() != acs->resampled_audio_frame_rate ()) { + _description->SetLabel (wxString::Format ( + _("Audio will be resampled from %.3fkHz to %.3fkHz."), - acs->content_audio_frame_rate() / 1000.0, - acs->output_audio_frame_rate() / 1000.0 ++ acs->audio_frame_rate() / 1000.0, ++ acs->resampled_audio_frame_rate() / 1000.0 + )); + } else { + _description->SetLabel (_("Audio will not be resampled.")); + } + } + -void -AudioPanel::setup_stream_description () -{ - FFmpegContentList fc = _editor->selected_ffmpeg_content (); - if (fc.size() != 1) { - _stream_description->SetLabel (""); - return; - } - - shared_ptr 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); - } -} - 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); } diff --cc src/wx/audio_panel.h index d5821d26a,fa18415bd..b0218575a --- a/src/wx/audio_panel.h +++ b/src/wx/audio_panel.h @@@ -42,15 -42,16 +42,18 @@@ private void show_clicked (); void stream_changed (); void mapping_changed (AudioMapping); + void processor_changed (); + void setup_processors (); + void setup_description (); - void setup_stream_description (); ContentSpinCtrlDouble* _gain; wxButton* _gain_calculate_button; wxButton* _show; ContentSpinCtrl* _delay; wxChoice* _stream; + wxChoice* _processor; + wxStaticText* _stream_description; AudioMappingView* _mapping; + wxStaticText* _description; AudioDialog* _audio_dialog; }; diff --cc src/wx/film_viewer.cc index 7ecba1903,54cd3e77d..a46983a6f --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@@ -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 > 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 diff --cc wscript index fccf53245,62b47d9a4..6b55dbdad --- a/wscript +++ 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