+2014-08-06 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.1 released.
+
+2014-07-15 Carl Hetherington <cth@carlh.net>
+
+ * A variety of changes were made on the 2.0 branch
+ but not documented in the ChangeLog. Most sigificantly:
+
+ - DCP import
+ - Creation of DCPs with proper XML subtitles
+ - Import of .srt and .xml subtitles
+ - Audio processing framework (with some basic processors).
+
+2014-03-07 Carl Hetherington <cth@carlh.net>
+
+ * Add subtitle view.
+
+ 2014-08-23 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.72.12 released.
+
+ 2014-08-23 Carl Hetherington <cth@carlh.net>
+
+ * Revert previous use of AVFormatContext::start_time when
+ computing the length of video. I think this is wrong, and
+ causes bits to be missed off the end of videos (and other
+ problems).
+
+ 2014-08-20 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.72.11 released.
+
+ 2014-08-19 Carl Hetherington <cth@carlh.net>
+
+ * Attempt to fix random crashes on OS X (especially during encodes)
+ thought to be caused by multiple threads using (different) stringstreams
+ at the same time; see src/lib/safe_stringstream.
++>>>>>>> origin/master
+
2014-08-09 Carl Hetherington <cth@carlh.net>
* Version 1.72.10 released.
2014-07-10 Carl Hetherington <cth@carlh.net>
* Version 1.72.2 released.
+>>>>>>> origin/master
2014-07-10 Carl Hetherington <cth@carlh.net>
-dcpomatic (1.72.12-1) UNRELEASED; urgency=low
+dcpomatic (2.0.1-1) UNRELEASED; urgency=low
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
- <<<<<<< HEAD
- * New upstream release.
- * New upstream release.
- * New upstream release.
- * New upstream release.
- -- Carl Hetherington <carl@d1stkfactory> Sat, 23 Aug 2014 18:47:52 +0100
+ -- Carl Hetherington <carl@dalglish> Wed, 06 Aug 2014 18:59:35 +0100
- =======
- * New upstream release.
- * New upstream release.
-
- -- Carl Hetherington <carl@d1stkfactory> Sat, 09 Aug 2014 12:38:18 +0100
- >>>>>>> origin/master
dcpomatic (0.87-1) UNRELEASED; urgency=low
*/
#include "audio_analysis.h"
+#include "audio_buffers.h"
#include "analyse_audio_job.h"
#include "compose.hpp"
#include "film.h"
shared_ptr<Playlist> playlist (new Playlist);
playlist->add (content);
shared_ptr<Player> player (new Player (_film, playlist));
- player->disable_video ();
- player->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1, _2));
-
- _samples_per_point = max (int64_t (1), _film->time_to_audio_frames (_film->length()) / _num_points);
+ int64_t const len = _film->length().frames (_film->audio_frame_rate());
+ _samples_per_point = max (int64_t (1), len / _num_points);
_current.resize (_film->audio_channels ());
_analysis.reset (new AudioAnalysis (_film->audio_channels ()));
_done = 0;
- OutputAudioFrame const len = _film->time_to_audio_frames (_film->length ());
- while (!player->pass ()) {
- set_progress (double (_done) / len);
+ DCPTime const block = DCPTime::from_seconds (1.0 / 8);
+ for (DCPTime t; t < _film->length(); t += block) {
+ analyse (player->get_audio (t, block, false));
+ set_progress (t.seconds() / _film->length().seconds());
}
_analysis->write (content->audio_analysis_path ());
}
void
-AnalyseAudioJob::audio (shared_ptr<const AudioBuffers> b, Time)
+AnalyseAudioJob::analyse (shared_ptr<const AudioBuffers> b)
{
for (int i = 0; i < b->frames(); ++i) {
for (int j = 0; j < b->channels(); ++j) {
float s = b->data(j)[i];
if (fabsf (s) < 10e-7) {
- /* stringstream can't serialise and recover inf or -inf, so prevent such
+ /* SafeStringStream can't serialise and recover inf or -inf, so prevent such
values by replacing with this (140dB down) */
s = 10e-7;
}
*/
- #include <sstream>
#include <cstdlib>
#include <fstream>
#include <glib.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
-#include <libdcp/colour_matrix.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/colour_matrix.h>
+#include <dcp/raw_convert.h>
+#include <dcp/signer.h>
+#include <dcp/certificate_chain.h>
#include <libcxml/cxml.h>
#include "config.h"
#include "server.h"
#include "filter.h"
#include "ratio.h"
#include "dcp_content_type.h"
-#include "sound_processor.h"
+#include "cinema_sound_processor.h"
#include "colour_conversion.h"
#include "cinema.h"
#include "util.h"
+#include "cross.h"
#include "i18n.h"
using boost::optional;
using boost::algorithm::is_any_of;
using boost::algorithm::split;
-using libdcp::raw_convert;
+using dcp::raw_convert;
Config* Config::_instance = 0;
, _server_port_base (6192)
, _use_any_servers (true)
, _tms_path (".")
- , _sound_processor (SoundProcessor::from_id (N_("dolby_cp750")))
+ , _cinema_sound_processor (CinemaSoundProcessor::from_id (N_("dolby_cp750")))
, _allow_any_dcp_frame_rate (false)
, _default_still_length (10)
, _default_scale (Ratio::from_id ("185"))
_allowed_dcp_frame_rates.push_back (50);
_allowed_dcp_frame_rates.push_back (60);
- _colour_conversions.push_back (PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6));
- _colour_conversions.push_back (PresetColourConversion (_("sRGB non-linearised"), 2.4, false, libdcp::colour_matrix::srgb_to_xyz, 2.6));
- _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, libdcp::colour_matrix::rec709_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("sRGB"), 2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("sRGB non-linearised"), 2.4, false, dcp::colour_matrix::srgb_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, dcp::colour_matrix::rec709_to_xyz, 2.6));
reset_kdm_email ();
}
Config::read ()
{
if (!boost::filesystem::exists (file (false))) {
- read_old_metadata ();
return;
}
c = f.optional_string_child ("SoundProcessor");
if (c) {
- _sound_processor = SoundProcessor::from_id (c.get ());
+ _cinema_sound_processor = CinemaSoundProcessor::from_id (c.get ());
+ }
+ c = f.optional_string_child ("CinemaSoundProcessor");
+ if (c) {
+ _cinema_sound_processor = CinemaSoundProcessor::from_id (c.get ());
}
_language = f.optional_string_child ("Language");
/* Loading version 0 (before Rec. 709 was added as a preset).
Add it in.
*/
- _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, libdcp::colour_matrix::rec709_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, dcp::colour_matrix::rec709_to_xyz, 2.6));
}
list<cxml::NodePtr> cin = f.node_children ("Cinema");
_allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate");
_log_types = f.optional_number_child<int> ("LogTypes").get_value_or (Log::TYPE_GENERAL | Log::TYPE_WARNING | Log::TYPE_ERROR);
-}
-void
-Config::read_old_metadata ()
-{
- /* XXX: this won't work with non-Latin filenames */
- ifstream f (file(true).string().c_str ());
- string line;
-
- while (getline (f, line)) {
- if (line.empty ()) {
- continue;
+ cxml::NodePtr signer = f.optional_node_child ("Signer");
+ dcp::CertificateChain signer_chain;
+ if (signer) {
+ /* Read the signing certificates and private key in from the config file */
+ list<cxml::NodePtr> certificates = signer->node_children ("Certificate");
+ for (list<cxml::NodePtr>::const_iterator i = certificates.begin(); i != certificates.end(); ++i) {
+ signer_chain.add (dcp::Certificate ((*i)->content ()));
}
- if (line[0] == '#') {
- continue;
- }
+ _signer.reset (new dcp::Signer (signer_chain, signer->string_child ("PrivateKey")));
+ } else {
+ /* Make a new set of signing certificates and key */
+ _signer.reset (new dcp::Signer (openssl_path ()));
+ }
- size_t const s = line.find (' ');
- if (s == string::npos) {
- continue;
- }
-
- string const k = line.substr (0, s);
- string const v = line.substr (s + 1);
-
- if (k == N_("num_local_encoding_threads")) {
- _num_local_encoding_threads = atoi (v.c_str ());
- } else if (k == N_("default_directory")) {
- _default_directory = v;
- } else if (k == N_("server_port")) {
- _server_port_base = atoi (v.c_str ());
- } else if (k == N_("server")) {
- vector<string> b;
- split (b, v, is_any_of (" "));
- if (b.size() == 2) {
- _servers.push_back (b[0]);
- }
- } else if (k == N_("tms_ip")) {
- _tms_ip = v;
- } else if (k == N_("tms_path")) {
- _tms_path = v;
- } else if (k == N_("tms_user")) {
- _tms_user = v;
- } else if (k == N_("tms_password")) {
- _tms_password = v;
- } else if (k == N_("sound_processor")) {
- _sound_processor = SoundProcessor::from_id (v);
- } else if (k == "language") {
- _language = v;
- } else if (k == "default_container") {
- _default_container = Ratio::from_id (v);
- } else if (k == "default_dcp_content_type") {
- _default_dcp_content_type = DCPContentType::from_isdcf_name (v);
- } else if (k == "dcp_metadata_issuer") {
- _dcp_metadata.issuer = v;
- } else if (k == "dcp_metadata_creator") {
- _dcp_metadata.creator = v;
- } else if (k == "dcp_metadata_issue_date") {
- _dcp_metadata.issue_date = v;
- }
+ if (f.optional_string_child ("DecryptionCertificate")) {
+ _decryption_certificate = dcp::Certificate (f.string_child ("DecryptionCertificate"));
+ }
+
+ if (f.optional_string_child ("DecryptionPrivateKey")) {
+ _decryption_private_key = f.string_child ("DecryptionPrivateKey");
+ }
- _default_isdcf_metadata.read_old_metadata (k, v);
+ if (!f.optional_string_child ("DecryptionCertificate") || !f.optional_string_child ("DecryptionPrivateKey")) {
+ /* Generate our own decryption certificate and key if either is not present in config */
+ boost::filesystem::path p = dcp::make_certificate_chain (openssl_path ());
+ _decryption_certificate = dcp::Certificate (dcp::file_to_string (p / "leaf.signed.pem"));
+ _decryption_private_key = dcp::file_to_string (p / "leaf.key");
+ boost::filesystem::remove_all (p);
}
}
return p;
}
-boost::filesystem::path
-Config::signer_chain_directory () const
-{
- boost::filesystem::path p;
- p /= g_get_user_config_dir ();
- p /= "dcpomatic";
- p /= "crypt";
- boost::filesystem::create_directories (p);
- return p;
-}
-
/** @return Singleton instance */
Config *
Config::instance ()
root->add_child("TMSPath")->add_child_text (_tms_path);
root->add_child("TMSUser")->add_child_text (_tms_user);
root->add_child("TMSPassword")->add_child_text (_tms_password);
- if (_sound_processor) {
- root->add_child("SoundProcessor")->add_child_text (_sound_processor->id ());
+ if (_cinema_sound_processor) {
+ root->add_child("CinemaSoundProcessor")->add_child_text (_cinema_sound_processor->id ());
}
if (_language) {
root->add_child("Language")->add_child_text (_language.get());
root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
-
+
+ xmlpp::Element* signer = root->add_child ("Signer");
+ dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
+ for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
+ signer->add_child("Certificate")->add_child_text (i->certificate (true));
+ }
+ signer->add_child("PrivateKey")->add_child_text (_signer->key ());
+
+ root->add_child("DecryptionCertificate")->add_child_text (_decryption_certificate.certificate (true));
+ root->add_child("DecryptionPrivateKey")->add_child_text (_decryption_private_key);
+
doc.write_to_file_formatted (file(false).string ());
}
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ 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
*/
+/** @file src/lib/content.cc
+ * @brief Content class.
+ */
+
#include <boost/thread/mutex.hpp>
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "content.h"
#include "util.h"
#include "content_factory.h"
#include "ui_signaller.h"
#include "exceptions.h"
#include "film.h"
+ #include "safe_stringstream.h"
#include "i18n.h"
using std::string;
- using std::stringstream;
using std::set;
using std::list;
using std::cout;
using std::vector;
using std::max;
using boost::shared_ptr;
-using libdcp::raw_convert;
+using dcp::raw_convert;
int const ContentProperty::PATH = 400;
int const ContentProperty::POSITION = 401;
}
-Content::Content (shared_ptr<const Film> f, Time p)
+Content::Content (shared_ptr<const Film> f, DCPTime p)
: _film (f)
, _position (p)
, _trim_start (0)
_paths.push_back (p);
}
-Content::Content (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
+Content::Content (shared_ptr<const Film> f, cxml::ConstNodePtr node)
: _film (f)
, _change_signals_frequent (false)
{
_paths.push_back ((*i)->content ());
}
_digest = node->string_child ("Digest");
- _position = node->number_child<Time> ("Position");
- _trim_start = node->number_child<Time> ("TrimStart");
- _trim_end = node->number_child<Time> ("TrimEnd");
+ _position = DCPTime (node->number_child<double> ("Position"));
+ _trim_start = DCPTime (node->number_child<double> ("TrimStart"));
+ _trim_end = DCPTime (node->number_child<double> ("TrimEnd"));
}
Content::Content (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
, _change_signals_frequent (false)
{
for (size_t i = 0; i < c.size(); ++i) {
- if (i > 0 && c[i]->trim_start ()) {
+ if (i > 0 && c[i]->trim_start() > DCPTime()) {
throw JoinError (_("Only the first piece of content to be joined can have a start trim."));
}
- if (i < (c.size() - 1) && c[i]->trim_end ()) {
+ if (i < (c.size() - 1) && c[i]->trim_end () > DCPTime()) {
throw JoinError (_("Only the last piece of content to be joined can have an end trim."));
}
node->add_child("Path")->add_child_text (i->string ());
}
node->add_child("Digest")->add_child_text (_digest);
- node->add_child("Position")->add_child_text (raw_convert<string> (_position));
- node->add_child("TrimStart")->add_child_text (raw_convert<string> (_trim_start));
- node->add_child("TrimEnd")->add_child_text (raw_convert<string> (_trim_end));
+ node->add_child("Position")->add_child_text (raw_convert<string> (_position.get ()));
+ node->add_child("TrimStart")->add_child_text (raw_convert<string> (_trim_start.get ()));
+ node->add_child("TrimEnd")->add_child_text (raw_convert<string> (_trim_end.get ()));
}
void
}
void
-Content::set_position (Time p)
+Content::set_position (DCPTime p)
{
{
boost::mutex::scoped_lock lm (_mutex);
}
void
-Content::set_trim_start (Time t)
+Content::set_trim_start (DCPTime t)
{
{
boost::mutex::scoped_lock lm (_mutex);
}
void
-Content::set_trim_end (Time t)
+Content::set_trim_end (DCPTime t)
{
{
boost::mutex::scoped_lock lm (_mutex);
string
Content::technical_summary () const
{
- return String::compose ("%1 %2 %3", path_summary(), digest(), position());
+ return String::compose ("%1 %2 %3", path_summary(), digest(), position().seconds());
}
-Time
+DCPTime
Content::length_after_trim () const
{
- return max (int64_t (0), full_length() - trim_start() - trim_end());
-}
-
-/** @param t A time relative to the start of this content (not the position).
- * @return true if this time is trimmed by our trim settings.
- */
-bool
-Content::trimmed (Time t) const
-{
- return (t < trim_start() || t > (full_length() - trim_end ()));
+ return max (DCPTime (), full_length() - trim_start() - trim_end());
}
/** @return string which includes everything about how this content affects
string
Content::identifier () const
{
- stringstream s;
+ SafeStringStream s;
s << Content::digest()
- << "_" << position()
- << "_" << trim_start()
- << "_" << trim_end();
+ << "_" << position().get()
+ << "_" << trim_start().get()
+ << "_" << trim_end().get();
return s.str ();
}
--- /dev/null
- #include <sstream>
+/*
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+ Taken from code Copyright (C) 2010-2011 Terrence Meiczinger
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file src/dcp_video_frame.cc
+ * @brief A single frame of video destined for a DCP.
+ *
+ * Given an Image and some settings, this class knows how to encode
+ * the image to J2K either on the local host or on a remote server.
+ *
+ * Objects of this class are used for the queue that we keep
+ * of images that require encoding.
+ */
+
+#include <stdint.h>
+#include <cstring>
+#include <cstdlib>
+#include <stdexcept>
+#include <cstdio>
+#include <iomanip>
- using std::stringstream;
+#include <iostream>
+#include <fstream>
+#include <unistd.h>
+#include <errno.h>
+#include <boost/array.hpp>
+#include <boost/asio.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
+#include <dcp/gamma_lut.h>
+#include <dcp/xyz_frame.h>
+#include <dcp/rgb_xyz.h>
+#include <dcp/colour_matrix.h>
+#include <dcp/raw_convert.h>
+#include <libcxml/cxml.h>
+#include "film.h"
+#include "dcp_video.h"
+#include "config.h"
+#include "exceptions.h"
+#include "server.h"
+#include "util.h"
+#include "scaler.h"
+#include "image.h"
+#include "log.h"
+#include "cross.h"
+#include "player_video.h"
+#include "encoded_data.h"
+
+#define LOG_GENERAL(...) _log->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+
+#include "i18n.h"
+
+using std::string;
- stringstream xml;
- doc.write_to_stream (xml, "UTF-8");
- socket->write (xml.str().length() + 1);
- socket->write ((uint8_t *) xml.str().c_str(), xml.str().length() + 1);
+using std::cout;
+using boost::shared_ptr;
+using boost::lexical_cast;
+using dcp::Size;
+using dcp::raw_convert;
+
+#define DCI_COEFFICENT (48.0 / 52.37)
+
+/** Construct a DCP video frame.
+ * @param frame Input frame.
+ * @param index Index of the frame within the DCP.
+ * @param bw J2K bandwidth to use (see Config::j2k_bandwidth ())
+ * @param l Log to write to.
+ */
+DCPVideo::DCPVideo (
+ shared_ptr<const PlayerVideo> frame, int index, int dcp_fps, int bw, Resolution r, bool b, shared_ptr<Log> l
+ )
+ : _frame (frame)
+ , _index (index)
+ , _frames_per_second (dcp_fps)
+ , _j2k_bandwidth (bw)
+ , _resolution (r)
+ , _burn_subtitles (b)
+ , _log (l)
+{
+
+}
+
+DCPVideo::DCPVideo (shared_ptr<const PlayerVideo> frame, shared_ptr<const cxml::Node> node, shared_ptr<Log> log)
+ : _frame (frame)
+ , _log (log)
+{
+ _index = node->number_child<int> ("Index");
+ _frames_per_second = node->number_child<int> ("FramesPerSecond");
+ _j2k_bandwidth = node->number_child<int> ("J2KBandwidth");
+ _resolution = Resolution (node->optional_number_child<int>("Resolution").get_value_or (RESOLUTION_2K));
+ _burn_subtitles = node->bool_child ("BurnSubtitles");
+}
+
+/** J2K-encode this frame on the local host.
+ * @return Encoded data.
+ */
+shared_ptr<EncodedData>
+DCPVideo::encode_locally ()
+{
+ shared_ptr<dcp::GammaLUT> in_lut = dcp::GammaLUT::cache.get (
+ 12, _frame->colour_conversion().input_gamma, _frame->colour_conversion().input_gamma_linearised
+ );
+
+ /* XXX: libdcp should probably use boost */
+
+ double matrix[3][3];
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ matrix[i][j] = _frame->colour_conversion().matrix (i, j);
+ }
+ }
+
+ shared_ptr<dcp::XYZFrame> xyz = dcp::rgb_to_xyz (
+ _frame->image (_burn_subtitles),
+ in_lut,
+ dcp::GammaLUT::cache.get (16, 1 / _frame->colour_conversion().output_gamma, false),
+ matrix
+ );
+
+ /* Set the max image and component sizes based on frame_rate */
+ int max_cs_len = ((float) _j2k_bandwidth) / 8 / _frames_per_second;
+ if (_frame->eyes() == EYES_LEFT || _frame->eyes() == EYES_RIGHT) {
+ /* In 3D we have only half the normal bandwidth per eye */
+ max_cs_len /= 2;
+ }
+ int const max_comp_size = max_cs_len / 1.25;
+
+ /* get a J2K compressor handle */
+ opj_cinfo_t* cinfo = opj_create_compress (CODEC_J2K);
+ if (cinfo == 0) {
+ throw EncodeError (N_("could not create JPEG2000 encoder"));
+ }
+
+ /* Set encoding parameters to default values */
+ opj_cparameters_t parameters;
+ opj_set_default_encoder_parameters (¶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 ();
+}
+
#include <libavformat/avformat.h>
}
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "ffmpeg_content.h"
#include "ffmpeg_examiner.h"
+#include "ffmpeg_subtitle_stream.h"
+#include "ffmpeg_audio_stream.h"
#include "compose.hpp"
#include "job.h"
#include "util.h"
#include "log.h"
#include "exceptions.h"
#include "frame_rate_change.h"
+ #include "safe_stringstream.h"
#include "i18n.h"
#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
using std::string;
- using std::stringstream;
using std::vector;
using std::list;
using std::cout;
using std::pair;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
-using libdcp::raw_convert;
+using dcp::raw_convert;
int const FFmpegContentProperty::SUBTITLE_STREAMS = 100;
int const FFmpegContentProperty::SUBTITLE_STREAM = 101;
}
-FFmpegContent::FFmpegContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version, list<string>& notes)
+FFmpegContent::FFmpegContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version, list<string>& notes)
: Content (f, node)
, VideoContent (f, node, version)
, AudioContent (f, node)
for (size_t i = 0; i < c.size(); ++i) {
shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (c[i]);
- if (f->with_subtitles() && *(fc->_subtitle_stream.get()) != *(ref->_subtitle_stream.get())) {
+ if (fc->use_subtitles() && *(fc->_subtitle_stream.get()) != *(ref->_subtitle_stream.get())) {
throw JoinError (_("Content to be joined must use the same subtitle stream."));
}
}
if (_first_video) {
- node->add_child("FirstVideo")->add_child_text (raw_convert<string> (_first_video.get ()));
+ node->add_child("FirstVideo")->add_child_text (raw_convert<string> (_first_video.get().get()));
}
}
Content::examine (job);
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
shared_ptr<FFmpegExaminer> examiner (new FFmpegExaminer (shared_from_this ()));
+ take_from_video_examiner (examiner);
- VideoContent::Frame video_length = 0;
- video_length = examiner->video_length ();
- LOG_GENERAL ("Video length obtained from header as %1 frames", video_length);
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
{
boost::mutex::scoped_lock lm (_mutex);
- _video_length = video_length;
-
_subtitle_streams = examiner->subtitle_streams ();
if (!_subtitle_streams.empty ()) {
_subtitle_stream = _subtitle_streams.front ();
_first_video = examiner->first_video ();
}
- take_from_video_examiner (examiner);
-
- signal_changed (ContentProperty::LENGTH);
signal_changed (FFmpegContentProperty::SUBTITLE_STREAMS);
signal_changed (FFmpegContentProperty::SUBTITLE_STREAM);
signal_changed (FFmpegContentProperty::AUDIO_STREAMS);
string
FFmpegContent::information () const
{
- if (video_length() == 0 || video_frame_rate() == 0) {
+ if (video_length() == ContentTime (0) || video_frame_rate() == 0) {
return "";
}
- stringstream s;
+ SafeStringStream s;
- s << String::compose (_("%1 frames; %2 frames per second"), video_length_after_3d_combine(), video_frame_rate()) << "\n";
+ s << String::compose (_("%1 frames; %2 frames per second"), video_length_after_3d_combine().frames (video_frame_rate()), video_frame_rate()) << "\n";
s << VideoContent::information ();
return s.str ();
signal_changed (FFmpegContentProperty::AUDIO_STREAM);
}
-AudioContent::Frame
+ContentTime
FFmpegContent::audio_length () const
{
- int const cafr = content_audio_frame_rate ();
- float const vfr = video_frame_rate ();
- VideoContent::Frame const vl = video_length_after_3d_combine ();
-
- boost::mutex::scoped_lock lm (_mutex);
- if (!_audio_stream) {
- return 0;
+ if (!audio_stream ()) {
+ return ContentTime ();
}
-
- return video_frames_to_audio_frames (vl, cafr, vfr);
+
+ return video_length ();
}
int
return 0;
}
- return _audio_stream->channels;
+ return _audio_stream->channels ();
}
int
-FFmpegContent::content_audio_frame_rate () const
+FFmpegContent::audio_frame_rate () const
{
boost::mutex::scoped_lock lm (_mutex);
return 0;
}
- return _audio_stream->frame_rate;
+ return _audio_stream->frame_rate ();
}
bool
return a._id != b._id;
}
-FFmpegStream::FFmpegStream (shared_ptr<const cxml::Node> node)
- : name (node->string_child ("Name"))
- , _id (node->number_child<int> ("Id"))
-{
-
-}
-
-void
-FFmpegStream::as_xml (xmlpp::Node* root) const
-{
- root->add_child("Name")->add_child_text (name);
- root->add_child("Id")->add_child_text (raw_convert<string> (_id));
-}
-
-FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node, int version)
- : FFmpegStream (node)
- , mapping (node->node_child ("Mapping"), version)
-{
- frame_rate = node->number_child<int> ("FrameRate");
- channels = node->number_child<int64_t> ("Channels");
- first_audio = node->optional_number_child<double> ("FirstAudio");
-}
-
-void
-FFmpegAudioStream::as_xml (xmlpp::Node* root) const
-{
- FFmpegStream::as_xml (root);
- root->add_child("FrameRate")->add_child_text (raw_convert<string> (frame_rate));
- root->add_child("Channels")->add_child_text (raw_convert<string> (channels));
- if (first_audio) {
- root->add_child("FirstAudio")->add_child_text (raw_convert<string> (first_audio.get ()));
- }
- mapping.as_xml (root->add_child("Mapping"));
-}
-
-bool
-FFmpegStream::uses_index (AVFormatContext const * fc, int index) const
-{
- size_t i = 0;
- while (i < fc->nb_streams) {
- if (fc->streams[i]->id == _id) {
- return int (i) == index;
- }
- ++i;
- }
-
- return false;
-}
-
-AVStream *
-FFmpegStream::stream (AVFormatContext const * fc) const
-{
- size_t i = 0;
- while (i < fc->nb_streams) {
- if (fc->streams[i]->id == _id) {
- return fc->streams[i];
- }
- ++i;
- }
-
- assert (false);
- return 0;
-}
-
-/** Construct a SubtitleStream from a value returned from to_string().
- * @param t String returned from to_string().
- * @param v State file version.
- */
-FFmpegSubtitleStream::FFmpegSubtitleStream (shared_ptr<const cxml::Node> node)
- : FFmpegStream (node)
-{
-
-}
-
-void
-FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const
-{
- FFmpegStream::as_xml (root);
-}
-
-Time
+DCPTime
FFmpegContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateChange frc (video_frame_rate (), film->video_frame_rate ());
- return video_length_after_3d_combine() * frc.factor() * TIME_HZ / film->video_frame_rate ();
+ return DCPTime (video_length_after_3d_combine(), FrameRateChange (video_frame_rate (), film->video_frame_rate ()));
}
AudioMapping
return AudioMapping ();
}
- return _audio_stream->mapping;
+ return _audio_stream->mapping ();
}
void
void
FFmpegContent::set_audio_mapping (AudioMapping m)
{
- audio_stream()->mapping = m;
- signal_changed (AudioContentProperty::AUDIO_MAPPING);
+ audio_stream()->set_mapping (m);
+ AudioContent::set_audio_mapping (m);
}
string
FFmpegContent::identifier () const
{
- stringstream s;
+ SafeStringStream s;
s << VideoContent::identifier();
p /= name;
return p;
}
+
+list<ContentTimePeriod>
+FFmpegContent::subtitles_during (ContentTimePeriod period, bool starting) const
+{
+ list<ContentTimePeriod> d;
+
+ shared_ptr<FFmpegSubtitleStream> stream = subtitle_stream ();
+ if (!stream) {
+ return d;
+ }
+
+ /* XXX: inefficient */
+ for (vector<ContentTimePeriod>::const_iterator i = stream->periods.begin(); i != stream->periods.end(); ++i) {
+ if ((starting && period.contains (i->from)) || (!starting && period.overlaps (*i))) {
+ d.push_back (*i);
+ }
+ }
+
+ return d;
+}
+
+bool
+FFmpegContent::has_subtitles () const
+{
+ return !subtitle_streams().empty ();
+}
#include <stdexcept>
#include <vector>
- #include <sstream>
#include <iomanip>
#include <iostream>
#include <stdint.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
-#include "film.h"
#include "filter.h"
#include "exceptions.h"
#include "image.h"
#include "util.h"
#include "log.h"
#include "ffmpeg_decoder.h"
+#include "ffmpeg_audio_stream.h"
+#include "ffmpeg_subtitle_stream.h"
#include "filter_graph.h"
#include "audio_buffers.h"
#include "ffmpeg_content.h"
-#include "image_proxy.h"
+#include "raw_image_proxy.h"
+#include "film.h"
+#include "timer.h"
#include "i18n.h"
-#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
-#define LOG_ERROR(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
-#define LOG_WARNING(...) film->log()->log (__VA_ARGS__, Log::TYPE_WARNING);
+#define LOG_GENERAL(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_ERROR(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
+#define LOG_WARNING(...) _video_content->film()->log()->log (__VA_ARGS__, Log::TYPE_WARNING);
using std::cout;
using std::string;
using std::list;
using std::min;
using std::pair;
+using std::make_pair;
using boost::shared_ptr;
using boost::optional;
using boost::dynamic_pointer_cast;
-using libdcp::Size;
+using dcp::Size;
-FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio)
- : Decoder (f)
- , VideoDecoder (f, c)
- , AudioDecoder (f, c)
- , SubtitleDecoder (f)
+FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log> log)
+ : VideoDecoder (c)
+ , AudioDecoder (c)
+ , SubtitleDecoder (c)
, FFmpeg (c)
- , _subtitle_codec_context (0)
- , _subtitle_codec (0)
- , _decode_video (video)
- , _decode_audio (audio)
- , _pts_offset (0)
- , _just_sought (false)
+ , _log (log)
{
- setup_subtitle ();
-
/* Audio and video frame PTS values may not start with 0. We want
to fiddle them so that:
Then we remove big initial gaps in PTS and we allow our
insertion of black frames to work.
- We will do:
- audio_pts_to_use = audio_pts_from_ffmpeg + pts_offset;
- video_pts_to_use = video_pts_from_ffmpeg + pts_offset;
+ We will do pts_to_use = pts_from_ffmpeg + pts_offset;
*/
- bool const have_video = video && c->first_video();
- bool const have_audio = audio && c->audio_stream() && c->audio_stream()->first_audio;
+ bool const have_video = c->first_video();
+ bool const have_audio = c->audio_stream () && c->audio_stream()->first_audio;
/* First, make one of them start at 0 */
/* Now adjust both so that the video pts starts on a frame */
if (have_video && have_audio) {
- double first_video = c->first_video().get() + _pts_offset;
- double const old_first_video = first_video;
-
- /* Round the first video up to a frame boundary */
- if (fabs (rint (first_video * c->video_frame_rate()) - first_video * c->video_frame_rate()) > 1e-6) {
- first_video = ceil (first_video * c->video_frame_rate()) / c->video_frame_rate ();
- }
-
- _pts_offset += first_video - old_first_video;
- }
-}
-
-FFmpegDecoder::~FFmpegDecoder ()
-{
- boost::mutex::scoped_lock lm (_mutex);
-
- if (_subtitle_codec_context) {
- avcodec_close (_subtitle_codec_context);
+ ContentTime first_video = c->first_video().get() + _pts_offset;
+ ContentTime const old_first_video = first_video;
+ _pts_offset += first_video.round_up (c->video_frame_rate ()) - old_first_video;
}
}
/* XXX: should we reset _packet.data and size after each *_decode_* call? */
- if (_decode_video) {
- while (decode_video_packet ()) {}
- }
+ while (decode_video_packet ()) {}
- if (_ffmpeg_content->audio_stream() && _decode_audio) {
+ if (_ffmpeg_content->audio_stream()) {
decode_audio_packet ();
+ AudioDecoder::flush ();
}
-
- /* Stop us being asked for any more data */
- _video_position = _ffmpeg_content->video_length_after_3d_combine ();
- _audio_position = _ffmpeg_content->audio_length ();
}
-void
+bool
FFmpegDecoder::pass ()
{
int r = av_read_frame (_format_context, &_packet);
/* Maybe we should fail here, but for now we'll just finish off instead */
char buf[256];
av_strerror (r, buf, sizeof(buf));
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
LOG_ERROR (N_("error on av_read_frame (%1) (%2)"), buf, r);
}
flush ();
- return;
+ return true;
}
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
int const si = _packet.stream_index;
-
- if (si == _video_stream && _decode_video) {
+
+ if (si == _video_stream) {
decode_video_packet ();
- } else if (_ffmpeg_content->audio_stream() && _ffmpeg_content->audio_stream()->uses_index (_format_context, si) && _decode_audio) {
+ } else if (_ffmpeg_content->audio_stream() && _ffmpeg_content->audio_stream()->uses_index (_format_context, si)) {
decode_audio_packet ();
- } else if (_ffmpeg_content->subtitle_stream() && _ffmpeg_content->subtitle_stream()->uses_index (_format_context, si) && film->with_subtitles ()) {
+ } else if (_ffmpeg_content->subtitle_stream() && _ffmpeg_content->subtitle_stream()->uses_index (_format_context, si)) {
decode_subtitle_packet ();
}
av_free_packet (&_packet);
+ return false;
}
/** @param data pointer to array of pointers to buffers.
}
void
-FFmpegDecoder::seek (VideoContent::Frame frame, bool accurate)
+FFmpegDecoder::seek (ContentTime time, bool accurate)
{
- double const time_base = av_q2d (_format_context->streams[_video_stream]->time_base);
-
- /* If we are doing an accurate seek, our initial shot will be 5 frames (5 being
- a number plucked from the air) earlier than we want to end up. The loop below
- will hopefully then step through to where we want to be.
+ VideoDecoder::seek (time, accurate);
+ AudioDecoder::seek (time, accurate);
+
+ /* If we are doing an `accurate' seek, we need to use pre-roll, as
+ we don't really know what the seek will give us.
*/
- int initial = frame;
- if (accurate) {
- initial -= 5;
- }
+ ContentTime pre_roll = accurate ? ContentTime::from_seconds (2) : ContentTime (0);
+ time -= pre_roll;
- if (initial < 0) {
- initial = 0;
- }
-
- /* Initial seek time in the stream's timebase */
- int64_t const initial_vt = ((initial / _ffmpeg_content->original_video_frame_rate()) - _pts_offset) / time_base;
-
- av_seek_frame (_format_context, _video_stream, initial_vt, AVSEEK_FLAG_BACKWARD);
-
- avcodec_flush_buffers (video_codec_context());
- if (_subtitle_codec_context) {
- avcodec_flush_buffers (_subtitle_codec_context);
- }
-
- /* This !accurate is piling hack upon hack; setting _just_sought to true
- even with accurate == true defeats our attempt to align the start
- of the video and audio. Here we disable that defeat when accurate == true
- i.e. when we are making a DCP rather than just previewing one.
- Ewww. This should be gone in 2.0.
+ /* XXX: it seems debatable whether PTS should be used here...
+ http://www.mjbshaw.com/2012/04/seeking-in-ffmpeg-know-your-timestamp.html
*/
- if (!accurate) {
- _just_sought = true;
- }
-
- _video_position = frame;
- if (frame == 0 || !accurate) {
- /* We're already there, or we're as close as we need to be */
- return;
- }
+ ContentTime const u = time - _pts_offset;
+ int64_t s = u.seconds() / av_q2d (_format_context->streams[_video_stream]->time_base);
- while (true) {
- int r = av_read_frame (_format_context, &_packet);
- if (r < 0) {
- return;
- }
+ if (_ffmpeg_content->audio_stream ()) {
+ s = min (
+ s, int64_t (u.seconds() / av_q2d (_ffmpeg_content->audio_stream()->stream(_format_context)->time_base))
+ );
+ }
- if (_packet.stream_index != _video_stream) {
- av_free_packet (&_packet);
- continue;
- }
-
- int finished = 0;
- r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet);
- if (r >= 0 && finished) {
- _video_position = rint (
- (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * _ffmpeg_content->original_video_frame_rate()
- );
+ av_seek_frame (_format_context, _video_stream, s, 0);
- if (_video_position >= (frame - 1)) {
- av_free_packet (&_packet);
- break;
- }
- }
-
- av_free_packet (&_packet);
+ avcodec_flush_buffers (video_codec_context());
+ if (audio_codec_context ()) {
+ avcodec_flush_buffers (audio_codec_context ());
+ }
+ if (subtitle_codec_context ()) {
+ avcodec_flush_buffers (subtitle_codec_context ());
}
-
- /* _video_position should be the next thing to be emitted, which will the one after the thing
- we just saw.
- */
- _video_position++;
}
void
int frame_finished;
int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, ©_packet);
+
if (decode_result < 0) {
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
LOG_ERROR ("avcodec_decode_audio4 failed (%1)", decode_result);
return;
}
if (frame_finished) {
-
- if (_audio_position == 0) {
- /* Where we are in the source, in seconds */
- double const pts = av_q2d (_format_context->streams[copy_packet.stream_index]->time_base)
- * av_frame_get_best_effort_timestamp(_frame) + _pts_offset;
-
- if (pts > 0) {
- /* Emit some silence */
- int64_t frames = pts * _ffmpeg_content->content_audio_frame_rate ();
- while (frames > 0) {
- int64_t const this_time = min (frames, (int64_t) _ffmpeg_content->content_audio_frame_rate() / 2);
-
- shared_ptr<AudioBuffers> silence (
- new AudioBuffers (_ffmpeg_content->audio_channels(), this_time)
- );
-
- silence->make_silent ();
- audio (silence, _audio_position);
- frames -= this_time;
- }
- }
- }
+ ContentTime const ct = ContentTime::from_seconds (
+ av_frame_get_best_effort_timestamp (_frame) *
+ av_q2d (_ffmpeg_content->audio_stream()->stream (_format_context)->time_base))
+ + _pts_offset;
int const data_size = av_samples_get_buffer_size (
0, audio_codec_context()->channels, _frame->nb_samples, audio_sample_format (), 1
);
-
- audio (deinterleave_audio (_frame->data, data_size), _audio_position);
+
+ audio (deinterleave_audio (_frame->data, data_size), ct);
}
copy_packet.data += decode_result;
shared_ptr<FilterGraph> graph;
list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin();
- while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
+ while (i != _filter_graphs.end() && !(*i)->can_process (dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
++i;
}
if (i == _filter_graphs.end ()) {
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- graph.reset (new FilterGraph (_ffmpeg_content, libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
+ graph.reset (new FilterGraph (_ffmpeg_content, dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
_filter_graphs.push_back (graph);
-
LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format);
} else {
graph = *i;
list<pair<shared_ptr<Image>, int64_t> > images = graph->process (_frame);
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
for (list<pair<shared_ptr<Image>, int64_t> >::iterator i = images.begin(); i != images.end(); ++i) {
shared_ptr<Image> image = i->first;
if (i->second != AV_NOPTS_VALUE) {
-
- double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset;
-
- if (_just_sought) {
- /* We just did a seek, so disable any attempts to correct for where we
- are / should be.
- */
- _video_position = rint (pts * _ffmpeg_content->original_video_frame_rate ());
- _just_sought = false;
- }
-
- double const next = _video_position / _ffmpeg_content->original_video_frame_rate();
- double const one_frame = 1 / _ffmpeg_content->original_video_frame_rate ();
- double delta = pts - next;
-
- while (delta > one_frame) {
- /* This PTS is more than one frame forward in time of where we think we should be; emit
- a black frame.
- */
-
- /* XXX: I think this should be a copy of the last frame... */
- boost::shared_ptr<Image> black (
- new Image (
- static_cast<AVPixelFormat> (_frame->format),
- libdcp::Size (video_codec_context()->width, video_codec_context()->height),
- true
- )
- );
-
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- black->make_black ();
- video (shared_ptr<ImageProxy> (new RawImageProxy (image, film->log())), false, _video_position);
- delta -= one_frame;
- }
-
- if (delta > -one_frame) {
- /* This PTS is within a frame of being right; emit this (otherwise it will be dropped) */
- video (shared_ptr<ImageProxy> (new RawImageProxy (image, film->log())), false, _video_position);
- }
-
+ double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset.seconds ();
+ video (
+ shared_ptr<ImageProxy> (new RawImageProxy (image, _video_content->film()->log())),
+ rint (pts * _ffmpeg_content->video_frame_rate ())
+ );
} else {
LOG_WARNING ("Dropping frame without PTS");
}
return true;
}
-
-
-void
-FFmpegDecoder::setup_subtitle ()
-{
- boost::mutex::scoped_lock lm (_mutex);
-
- if (!_ffmpeg_content->subtitle_stream()) {
- return;
- }
-
- _subtitle_codec_context = _ffmpeg_content->subtitle_stream()->stream(_format_context)->codec;
- if (_subtitle_codec_context == 0) {
- throw DecodeError (N_("could not find subtitle stream"));
- }
-
- _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
-
- if (_subtitle_codec == 0) {
- throw DecodeError (N_("could not find subtitle decoder"));
- }
-
- if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
- throw DecodeError (N_("could not open subtitle decoder"));
- }
-}
-
-bool
-FFmpegDecoder::done () const
-{
- bool const vd = !_decode_video || (_video_position >= _ffmpeg_content->video_length());
- bool const ad = !_decode_audio || !_ffmpeg_content->audio_stream() || (_audio_position >= _ffmpeg_content->audio_length());
- return vd && ad;
-}
void
FFmpegDecoder::decode_subtitle_packet ()
{
int got_subtitle;
AVSubtitle sub;
- if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) < 0 || !got_subtitle) {
+ if (avcodec_decode_subtitle2 (subtitle_codec_context(), &sub, &got_subtitle, &_packet) < 0 || !got_subtitle) {
return;
}
indicate that the previous subtitle should stop.
*/
if (sub.num_rects <= 0) {
- subtitle (shared_ptr<Image> (), dcpomatic::Rect<double> (), 0, 0);
+ image_subtitle (ContentTimePeriod (), shared_ptr<Image> (), dcpomatic::Rect<double> ());
return;
} else if (sub.num_rects > 1) {
throw DecodeError (_("multi-part subtitles not yet supported"));
}
- /* Subtitle PTS in seconds (within the source, not taking into account any of the
+ /* Subtitle PTS (within the source, not taking into account any of the
source that we may have chopped off for the DCP)
*/
- double const packet_time = (static_cast<double> (sub.pts ) / AV_TIME_BASE) + _pts_offset;
-
- /* hence start time for this sub */
- Time const from = (packet_time + (double (sub.start_display_time) / 1e3)) * TIME_HZ;
- Time const to = (packet_time + (double (sub.end_display_time) / 1e3)) * TIME_HZ;
+ ContentTimePeriod period = subtitle_period (sub) + _pts_offset;
AVSubtitleRect const * rect = sub.rects[0];
if (rect->type != SUBTITLE_BITMAP) {
- throw DecodeError (_("non-bitmap subtitles not yet supported"));
+ /* XXX */
+ // throw DecodeError (_("non-bitmap subtitles not yet supported"));
+ return;
}
/* Note RGBA is expressed little-endian, so the first byte in the word is R, second
G, third B, fourth A.
*/
- shared_ptr<Image> image (new Image (PIX_FMT_RGBA, libdcp::Size (rect->w, rect->h), true));
+ shared_ptr<Image> image (new Image (PIX_FMT_RGBA, dcp::Size (rect->w, rect->h), true));
/* Start of the first line in the subtitle */
uint8_t* sub_p = rect->pict.data[0];
out_p += image->stride()[0] / sizeof (uint32_t);
}
- libdcp::Size const vs = _ffmpeg_content->video_size ();
+ dcp::Size const vs = _ffmpeg_content->video_size ();
- subtitle (
+ image_subtitle (
+ period,
image,
dcpomatic::Rect<double> (
static_cast<double> (rect->x) / vs.width,
static_cast<double> (rect->y) / vs.height,
static_cast<double> (rect->w) / vs.width,
static_cast<double> (rect->h) / vs.height
- ),
- from,
- to
+ )
);
-
avsubtitle_free (&sub);
}
+
+list<ContentTimePeriod>
+FFmpegDecoder::subtitles_during (ContentTimePeriod p, bool starting) const
+{
+ return _ffmpeg_content->subtitles_during (p, starting);
+}
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ 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
}
#include "ffmpeg_examiner.h"
#include "ffmpeg_content.h"
+#include "ffmpeg_audio_stream.h"
+#include "ffmpeg_subtitle_stream.h"
+#include "util.h"
+ #include "safe_stringstream.h"
#include "i18n.h"
using std::string;
using std::cout;
using std::max;
- using std::stringstream;
using boost::shared_ptr;
using boost::optional;
}
}
- /* Run through until we find the first audio (for each stream) and video */
-
+ /* Run through until we find:
+ * - the first video.
+ * - the first audio for each stream.
+ * - the subtitle periods for each stream.
+ *
+ * We have to note subtitle periods as otherwise we have no way of knowing
+ * where we should look for subtitles (video and audio are always present,
+ * so they are ok).
+ */
while (true) {
int r = av_read_frame (_format_context, &_packet);
if (r < 0) {
break;
}
- int frame_finished;
-
AVCodecContext* context = _format_context->streams[_packet.stream_index]->codec;
- if (_packet.stream_index == _video_stream && !_first_video) {
- if (avcodec_decode_video2 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
- _first_video = frame_time (_format_context->streams[_video_stream]);
- }
- } else {
- for (size_t i = 0; i < _audio_streams.size(); ++i) {
- if (_audio_streams[i]->uses_index (_format_context, _packet.stream_index) && !_audio_streams[i]->first_audio) {
- if (avcodec_decode_audio4 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
- _audio_streams[i]->first_audio = frame_time (_audio_streams[i]->stream (_format_context));
- }
- }
+ if (_packet.stream_index == _video_stream) {
+ video_packet (context);
+ }
+
+ for (size_t i = 0; i < _audio_streams.size(); ++i) {
+ if (_audio_streams[i]->uses_index (_format_context, _packet.stream_index)) {
+ audio_packet (context, _audio_streams[i]);
}
}
- bool have_all_audio = true;
- size_t i = 0;
- while (i < _audio_streams.size() && have_all_audio) {
- have_all_audio = _audio_streams[i]->first_audio;
- ++i;
+ for (size_t i = 0; i < _subtitle_streams.size(); ++i) {
+ if (_subtitle_streams[i]->uses_index (_format_context, _packet.stream_index)) {
+ subtitle_packet (context, _subtitle_streams[i]);
+ }
}
av_free_packet (&_packet);
-
- if (_first_video && have_all_audio) {
- break;
+ }
+}
+
+void
+FFmpegExaminer::video_packet (AVCodecContext* context)
+{
+ if (_first_video) {
+ return;
+ }
+
+ int frame_finished;
+ if (avcodec_decode_video2 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
+ _first_video = frame_time (_format_context->streams[_video_stream]);
+ }
+}
+
+void
+FFmpegExaminer::audio_packet (AVCodecContext* context, shared_ptr<FFmpegAudioStream> stream)
+{
+ if (stream->first_audio) {
+ return;
+ }
+
+ int frame_finished;
+ if (avcodec_decode_audio4 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
+ stream->first_audio = frame_time (stream->stream (_format_context));
+ }
+}
+
+void
+FFmpegExaminer::subtitle_packet (AVCodecContext* context, shared_ptr<FFmpegSubtitleStream> stream)
+{
+ int frame_finished;
+ AVSubtitle sub;
+ if (avcodec_decode_subtitle2 (context, &sub, &frame_finished, &_packet) >= 0 && frame_finished) {
+ ContentTimePeriod const period = subtitle_period (sub);
+ if (sub.num_rects == 0 && !stream->periods.empty () && stream->periods.back().to > period.from) {
+ /* Finish the last subtitle */
+ stream->periods.back().to = period.from;
+ } else if (sub.num_rects == 1) {
+ stream->periods.push_back (period);
}
}
}
-optional<double>
+optional<ContentTime>
FFmpegExaminer::frame_time (AVStream* s) const
{
- optional<double> t;
+ optional<ContentTime> t;
int64_t const bet = av_frame_get_best_effort_timestamp (_frame);
if (bet != AV_NOPTS_VALUE) {
- t = bet * av_q2d (s->time_base);
+ t = ContentTime::from_seconds (bet * av_q2d (s->time_base));
}
return t;
float
FFmpegExaminer::video_frame_rate () const
{
- AVStream* s = _format_context->streams[_video_stream];
-
- if (s->avg_frame_rate.num && s->avg_frame_rate.den) {
- return av_q2d (s->avg_frame_rate);
- }
-
- return av_q2d (s->r_frame_rate);
+ /* This use of r_frame_rate is debateable; there's a few different
+ * frame rates in the format context, but this one seems to be the most
+ * reliable.
+ */
+ return av_q2d (av_stream_get_r_frame_rate (_format_context->streams[_video_stream]));
}
-libdcp::Size
+dcp::Size
FFmpegExaminer::video_size () const
{
- return libdcp::Size (video_codec_context()->width, video_codec_context()->height);
+ return dcp::Size (video_codec_context()->width, video_codec_context()->height);
}
-/** @return Length (in video frames) according to our content's header */
-VideoContent::Frame
+/** @return Length according to our content's header */
+ContentTime
FFmpegExaminer::video_length () const
{
- ContentTime const length = ContentTime::from_seconds (double (_format_context->duration - _format_context->start_time) / AV_TIME_BASE);
- VideoContent::Frame const length = (double (_format_context->duration) / AV_TIME_BASE) * video_frame_rate();
- return max (1, length);
++ ContentTime const length = ContentTime::from_seconds (double (_format_context->duration) / AV_TIME_BASE);
+ return ContentTime (max (ContentTime::Type (1), length.get ()));
}
string
FFmpegExaminer::audio_stream_name (AVStream* s) const
{
- stringstream n;
+ SafeStringStream n;
n << stream_name (s);
string
FFmpegExaminer::subtitle_stream_name (AVStream* s) const
{
- stringstream n;
+ SafeStringStream n;
n << stream_name (s);
string
FFmpegExaminer::stream_name (AVStream* s) const
{
- stringstream n;
+ SafeStringStream n;
if (s->metadata) {
AVDictionaryEntry const * lang = av_dict_get (s->metadata, "language", 0, 0);
#include <algorithm>
#include <fstream>
#include <cstdlib>
- #include <sstream>
#include <iomanip>
#include <unistd.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
-#include <boost/date_time.hpp>
+#include <boost/lexical_cast.hpp>
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
-#include <libdcp/signer_chain.h>
-#include <libdcp/cpl.h>
-#include <libdcp/signer.h>
-#include <libdcp/util.h>
-#include <libdcp/kdm.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/cpl.h>
+#include <dcp/signer.h>
+#include <dcp/util.h>
+#include <dcp/local_time.h>
+#include <dcp/raw_convert.h>
#include "film.h"
#include "job.h"
#include "util.h"
#include "ratio.h"
#include "cross.h"
#include "cinema.h"
+ #include "safe_stringstream.h"
#include "i18n.h"
using std::string;
- using std::stringstream;
using std::multimap;
using std::pair;
using std::map;
using boost::starts_with;
using boost::optional;
using boost::is_any_of;
-using libdcp::Size;
-using libdcp::Signer;
-using libdcp::raw_convert;
+using dcp::Size;
+using dcp::Signer;
+using dcp::raw_convert;
+using dcp::raw_convert;
#define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
#define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, Log::TYPE_GENERAL);
* Use <Scale> tag in <VideoContent> rather than <Ratio>.
* 8 -> 9
* DCI -> ISDCF
+ *
+ * Bumped to 32 for 2.0 branch; some times are expressed in Times rather
+ * than frames now.
*/
-int const Film::current_state_version = 9;
+int const Film::current_state_version = 32;
/** Construct a Film object in a given directory.
*
, _container (Config::instance()->default_container ())
, _resolution (RESOLUTION_2K)
, _scaler (Scaler::from_id ("bicubic"))
- , _with_subtitles (false)
, _signed (true)
, _encrypted (false)
, _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
, _three_d (false)
, _sequence_video (true)
, _interop (false)
+ , _burn_subtitles (false)
, _state_version (current_state_version)
, _dirty (false)
{
{
assert (container ());
- stringstream s;
+ SafeStringStream s;
s.imbue (std::locale::classic ());
s << container()->id()
s << "_S";
}
- if (_three_d) {
- s << "_3D";
+ if (_burn_subtitles) {
+ s << "_B";
}
- if (_with_subtitles) {
- s << "_WS";
+ if (_three_d) {
+ s << "_3D";
}
return s.str ();
return filename_safe_name() + "_audio.mxf";
}
+boost::filesystem::path
+Film::subtitle_xml_filename () const
+{
+ return filename_safe_name() + "_subtitle.xml";
+}
+
string
Film::filename_safe_name () const
{
root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
root->add_child("Scaler")->add_child_text (_scaler->id ());
- root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0");
root->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
_isdcf_metadata.as_xml (root->add_child ("ISDCFMetadata"));
root->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
root->add_child("SequenceVideo")->add_child_text (_sequence_video ? "1" : "0");
root->add_child("Interop")->add_child_text (_interop ? "1" : "0");
+ root->add_child("BurnSubtitles")->add_child_text (_burn_subtitles ? "1" : "0");
root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
root->add_child("Key")->add_child_text (_key.hex ());
_resolution = string_to_resolution (f.string_child ("Resolution"));
_scaler = Scaler::from_id (f.string_child ("Scaler"));
- _with_subtitles = f.bool_child ("WithSubtitles");
_j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
_video_frame_rate = f.number_child<int> ("VideoFrameRate");
_signed = f.optional_bool_child("Signed").get_value_or (true);
_sequence_video = f.bool_child ("SequenceVideo");
_three_d = f.bool_child ("ThreeD");
_interop = f.bool_child ("Interop");
- _key = libdcp::Key (f.string_child ("Key"));
+ if (_state_version >= 32) {
+ _burn_subtitles = f.bool_child ("BurnSubtitles");
+ }
+ _key = dcp::Key (f.string_child ("Key"));
list<string> notes;
/* This method is the only one that can return notes (so far) */
string
Film::isdcf_name (bool if_created_now) const
{
- stringstream d;
+ SafeStringStream d;
string raw_name = name ();
*/
/* The standard says we don't do this for trailers, for some strange reason */
- if (dcp_content_type() && dcp_content_type()->libdcp_kind() != libdcp::TRAILER) {
+ if (dcp_content_type() && dcp_content_type()->libdcp_kind() != dcp::TRAILER) {
ContentList cl = content ();
Ratio const * content_ratio = 0;
for (ContentList::const_iterator i = cl.begin(); i != cl.end(); ++i) {
return name();
}
-
void
Film::set_directory (boost::filesystem::path d)
{
signal_changed (SCALER);
}
-void
-Film::set_with_subtitles (bool w)
-{
- _with_subtitles = w;
- signal_changed (WITH_SUBTITLES);
-}
-
void
Film::set_j2k_bandwidth (int b)
{
signal_changed (INTEROP);
}
+void
+Film::set_burn_subtitles (bool b)
+{
+ _burn_subtitles = b;
+ signal_changed (BURN_SUBTITLES);
+}
+
void
Film::signal_changed (Property p)
{
boost::filesystem::path p;
p /= info_dir ();
- stringstream s;
+ SafeStringStream s;
s.width (8);
s << setfill('0') << f;
p /= "j2c";
p /= video_identifier ();
- stringstream s;
+ SafeStringStream s;
s.width (8);
s << setfill('0') << f;
return file (p);
}
-/** Find all the DCPs in our directory that can be libdcp::DCP::read() and return details of their CPLs */
+/** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
vector<CPLSummary>
Film::cpls () const
{
) {
try {
- libdcp::DCP dcp (*i);
+ dcp::DCP dcp (*i);
dcp.read ();
out.push_back (
CPLSummary (
- i->path().leaf().string(), dcp.cpls().front()->id(), dcp.cpls().front()->name(), dcp.cpls().front()->filename()
+ i->path().leaf().string(),
+ dcp.cpls().front()->id(),
+ dcp.cpls().front()->annotation_text(),
+ dcp.cpls().front()->file()
)
);
} catch (...) {
return _playlist->content ();
}
+void
+Film::examine_content (shared_ptr<Content> c)
+{
+ shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
+ JobManager::instance()->add (j);
+}
+
void
Film::examine_and_add_content (shared_ptr<Content> c)
{
_playlist->move_later (c);
}
-Time
+DCPTime
Film::length () const
{
return _playlist->length ();
}
-bool
-Film::has_subtitles () const
+int
+Film::best_video_frame_rate () const
{
- return _playlist->has_subtitles ();
+ return _playlist->best_dcp_frame_rate ();
}
-OutputVideoFrame
-Film::best_video_frame_rate () const
+FrameRateChange
+Film::active_frame_rate_change (DCPTime t) const
{
- return _playlist->best_dcp_frame_rate ();
+ return _playlist->active_frame_rate_change (t, video_frame_rate ());
}
void
signal_changed (CONTENT);
}
-OutputAudioFrame
-Film::time_to_audio_frames (Time t) const
-{
- return divide_with_round (t * audio_frame_rate (), TIME_HZ);
-}
-
-OutputVideoFrame
-Film::time_to_video_frames (Time t) const
-{
- return divide_with_round (t * video_frame_rate (), TIME_HZ);
-}
-
-Time
-Film::audio_frames_to_time (OutputAudioFrame f) const
-{
- return divide_with_round (f * TIME_HZ, audio_frame_rate ());
-}
-
-Time
-Film::video_frames_to_time (OutputVideoFrame f) const
-{
- return divide_with_round (f * TIME_HZ, video_frame_rate ());
-}
-
-OutputAudioFrame
+int
Film::audio_frame_rate () const
{
/* XXX */
}
/** @return Size of the largest possible image in whatever resolution we are using */
-libdcp::Size
+dcp::Size
Film::full_frame () const
{
switch (_resolution) {
case RESOLUTION_2K:
- return libdcp::Size (2048, 1080);
+ return dcp::Size (2048, 1080);
case RESOLUTION_4K:
- return libdcp::Size (4096, 2160);
+ return dcp::Size (4096, 2160);
}
assert (false);
- return libdcp::Size ();
+ return dcp::Size ();
}
/** @return Size of the frame */
-libdcp::Size
+dcp::Size
Film::frame_size () const
{
- return fit_ratio_within (container()->ratio(), full_frame ());
+ return fit_ratio_within (container()->ratio(), full_frame (), 1);
}
-/** @param from KDM from time in local time.
- * @param to KDM to time in local time.
- */
-libdcp::KDM
+dcp::EncryptedKDM
Film::make_kdm (
- shared_ptr<libdcp::Certificate> target,
+ dcp::Certificate target,
boost::filesystem::path cpl_file,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime until,
+ dcp::Formulation formulation
) const
{
- shared_ptr<const Signer> signer = make_signer ();
-
- time_t now = time (0);
- struct tm* tm = localtime (&now);
- string const issue_date = libdcp::tm_to_string (tm);
+ shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
+ shared_ptr<const dcp::Signer> signer = Config::instance()->signer();
+ if (!signer->valid ()) {
+ throw InvalidSignerError ();
+ }
- return libdcp::KDM (cpl_file, signer, target, key (), from, until, "DCP-o-matic", issue_date, formulation);
+ return dcp::DecryptedKDM (
+ cpl, key(), from, until, "DCP-o-matic", cpl->content_title_text(), dcp::LocalTime().as_string()
+ ).encrypt (signer, target, formulation);
}
-list<libdcp::KDM>
+list<dcp::EncryptedKDM>
Film::make_kdms (
list<shared_ptr<Screen> > screens,
boost::filesystem::path dcp,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime until,
+ dcp::Formulation formulation
) const
{
- list<libdcp::KDM> kdms;
+ list<dcp::EncryptedKDM> kdms;
for (list<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) {
- kdms.push_back (make_kdm ((*i)->certificate, dcp, from, until, formulation));
+ if ((*i)->certificate) {
+ kdms.push_back (make_kdm ((*i)->certificate.get(), dcp, from, until, formulation));
+ }
}
return kdms;
uint64_t
Film::required_disk_space () const
{
- return uint64_t (j2k_bandwidth() / 8) * length() / TIME_HZ;
+ return uint64_t (j2k_bandwidth() / 8) * length().seconds();
}
/** This method checks the disk that the Film is on and tries to decide whether or not
available = double (s.available) / 1073741824.0f;
return (available - required) > 1;
}
-
-FrameRateChange
-Film::active_frame_rate_change (Time t) const
-{
- return _playlist->active_frame_rate_change (t, video_frame_rate ());
-}
-
#include "exceptions.h"
#include "image.h"
#include "ffmpeg_content.h"
+ #include "safe_stringstream.h"
#include "i18n.h"
- using std::stringstream;
using std::string;
using std::list;
using std::pair;
using std::cout;
using boost::shared_ptr;
using boost::weak_ptr;
-using libdcp::Size;
+using dcp::Size;
/** Construct a FilterGraph for the settings in a piece of content.
* @param content Content.
* @param s Size of the images to process.
* @param p Pixel format of the images to process.
*/
-FilterGraph::FilterGraph (shared_ptr<const FFmpegContent> content, libdcp::Size s, AVPixelFormat p)
- : _buffer_src_context (0)
+FilterGraph::FilterGraph (shared_ptr<const FFmpegContent> content, dcp::Size s, AVPixelFormat p)
+ : _copy (false)
+ , _buffer_src_context (0)
, _buffer_sink_context (0)
, _size (s)
, _pixel_format (p)
+ , _frame (0)
{
- _frame = av_frame_alloc ();
-
- string filters = Filter::ffmpeg_string (content->filters());
+ string const filters = Filter::ffmpeg_string (content->filters());
if (filters.empty ()) {
- filters = "copy";
+ _copy = true;
+ return;
}
+ _frame = av_frame_alloc ();
+
AVFilterGraph* graph = avfilter_graph_alloc();
if (graph == 0) {
throw DecodeError (N_("could not create filter graph."));
throw DecodeError (N_("Could not create buffer sink filter"));
}
- stringstream a;
+ SafeStringStream a;
a << "video_size=" << _size.width << "x" << _size.height << ":"
<< "pix_fmt=" << _pixel_format << ":"
<< "time_base=1/1:"
throw DecodeError (N_("could not configure filter graph."));
}
- /* XXX: leaking `inputs' / `outputs' ? */
+ avfilter_inout_free (&inputs);
+ avfilter_inout_free (&outputs);
}
FilterGraph::~FilterGraph ()
{
- av_frame_free (&_frame);
+ if (_frame) {
+ av_frame_free (&_frame);
+ }
}
/** Take an AVFrame and process it using our configured filters, returning a
{
list<pair<shared_ptr<Image>, int64_t> > images;
- if (av_buffersrc_write_frame (_buffer_src_context, frame) < 0) {
- throw DecodeError (N_("could not push buffer into filter chain."));
- }
-
- while (true) {
- if (av_buffersink_get_frame (_buffer_sink_context, _frame) < 0) {
- break;
+ if (_copy) {
+ images.push_back (make_pair (shared_ptr<Image> (new Image (frame)), av_frame_get_best_effort_timestamp (frame)));
+ } else {
+ if (av_buffersrc_write_frame (_buffer_src_context, frame) < 0) {
+ throw DecodeError (N_("could not push buffer into filter chain."));
+ }
+
+ while (true) {
+ if (av_buffersink_get_frame (_buffer_sink_context, _frame) < 0) {
+ break;
+ }
+
+ images.push_back (make_pair (shared_ptr<Image> (new Image (_frame)), av_frame_get_best_effort_timestamp (_frame)));
+ av_frame_unref (_frame);
}
-
- images.push_back (make_pair (shared_ptr<Image> (new Image (_frame)), av_frame_get_best_effort_timestamp (_frame)));
- av_frame_unref (_frame);
}
-
+
return images;
}
* @return true if this chain can process images with `s' and `p', otherwise false.
*/
bool
-FilterGraph::can_process (libdcp::Size s, AVPixelFormat p) const
+FilterGraph::can_process (dcp::Size s, AVPixelFormat p) const
{
return (_size == s && _pixel_format == p);
}
#include <libcxml/cxml.h>
#include "image_content.h"
#include "image_examiner.h"
-#include "config.h"
#include "compose.hpp"
#include "film.h"
#include "job.h"
#include "frame_rate_change.h"
+#include "exceptions.h"
+ #include "safe_stringstream.h"
#include "i18n.h"
using std::string;
using std::cout;
- using std::stringstream;
using boost::shared_ptr;
ImageContent::ImageContent (shared_ptr<const Film> f, boost::filesystem::path p)
}
-ImageContent::ImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
+ImageContent::ImageContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
: Content (f, node)
, VideoContent (f, node, version)
{
assert (film);
shared_ptr<ImageExaminer> examiner (new ImageExaminer (film, shared_from_this(), job));
-
take_from_video_examiner (examiner);
- set_video_length (examiner->video_length ());
}
void
-ImageContent::set_video_length (VideoContent::Frame len)
+ImageContent::set_video_length (ContentTime len)
{
{
boost::mutex::scoped_lock lm (_mutex);
signal_changed (ContentProperty::LENGTH);
}
-Time
+DCPTime
ImageContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateChange frc (video_frame_rate(), film->video_frame_rate ());
- return video_length_after_3d_combine() * frc.factor() * TIME_HZ / video_frame_rate();
+ return DCPTime (video_length_after_3d_combine(), FrameRateChange (video_frame_rate(), film->video_frame_rate()));
}
string
ImageContent::identifier () const
{
- stringstream s;
+ SafeStringStream s;
s << VideoContent::identifier ();
- s << "_" << video_length();
+ s << "_" << video_length().get();
return s.str ();
}
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <boost/thread.hpp>
#include <boost/filesystem.hpp>
-#include <libdcp/exceptions.h>
+#include <dcp/exceptions.h>
#include "job.h"
#include "util.h"
#include "cross.h"
#include "ui_signaller.h"
#include "exceptions.h"
-#include "safe_stringstream.h"
+#include "film.h"
+#include "log.h"
#include "i18n.h"
using std::string;
using std::list;
using std::cout;
- using std::stringstream;
using boost::shared_ptr;
Job::Job (shared_ptr<const Film> f)
run ();
- } catch (libdcp::FileError& e) {
-
+ } catch (dcp::FileError& e) {
+
string m = String::compose (_("An error occurred whilst handling the file %1."), boost::filesystem::path (e.filename()).leaf());
try {
}
}
-/** @return Time (in seconds) that this sub-job has been running */
+/** @return DCPTime (in seconds) that this sub-job has been running */
int
Job::elapsed_time () const
{
void
Job::set_error (string s, string d)
{
+ _film->log()->log (String::compose ("Error in job: %1 (%2)", s, d), Log::TYPE_ERROR);
boost::mutex::scoped_lock lm (_state_mutex);
_error_summary = s;
_error_details = d;
pc = 99;
}
- stringstream s;
+ SafeStringStream s;
if (!finished ()) {
s << pc << N_("%");
if (p >= 0 && t > 10 && r > 0) {
#include <boost/shared_ptr.hpp>
#include <quickmail.h>
#include <zip.h>
-#include <libdcp/kdm.h>
+#include <dcp/encrypted_kdm.h>
+#include <dcp/types.h>
#include "kdm.h"
#include "cinema.h"
#include "exceptions.h"
#include "util.h"
#include "film.h"
#include "config.h"
+ #include "safe_stringstream.h"
using std::list;
using std::string;
- using std::stringstream;
using std::cout;
using boost::shared_ptr;
struct ScreenKDM
{
- ScreenKDM (shared_ptr<Screen> s, libdcp::KDM k)
+ ScreenKDM (shared_ptr<Screen> s, dcp::EncryptedKDM k)
: screen (s)
, kdm (k)
{}
shared_ptr<Screen> screen;
- libdcp::KDM kdm;
+ dcp::EncryptedKDM kdm;
};
static string
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation
)
{
- list<libdcp::KDM> kdms = film->make_kdms (screens, cpl, from, to, formulation);
+ list<dcp::EncryptedKDM> kdms = film->make_kdms (screens, cpl, from, to, formulation);
list<ScreenKDM> screen_kdms;
list<shared_ptr<Screen> >::iterator i = screens.begin ();
- list<libdcp::KDM>::iterator j = kdms.begin ();
+ list<dcp::EncryptedKDM>::iterator j = kdms.begin ();
while (i != screens.end() && j != kdms.end ()) {
screen_kdms.push_back (ScreenKDM (*i, *j));
++i;
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation
)
{
list<ScreenKDM> screen_kdms = make_screen_kdms (film, screens, cpl, from, to, formulation);
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation,
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation,
boost::filesystem::path directory
)
{
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation,
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation,
boost::filesystem::path directory
)
{
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation
)
{
list<CinemaKDMs> cinema_kdms = make_cinema_kdms (film, screens, cpl, from, to, formulation);
quickmail_initialize ();
- stringstream start;
+ SafeStringStream start;
start << from.date() << " " << from.time_of_day();
- stringstream end;
+ SafeStringStream end;
end << to.date() << " " << to.time_of_day();
string subject = Config::instance()->kdm_subject();
boost::algorithm::replace_all (body, "$END_TIME", end.str ());
boost::algorithm::replace_all (body, "$CINEMA_NAME", i->cinema->name);
- stringstream screens;
+ SafeStringStream screens;
for (list<ScreenKDM>::const_iterator j = i->screen_kdms.begin(); j != i->screen_kdms.end(); ++j) {
screens << j->screen->name << ", ";
}
#include <string>
#include <vector>
- #include <sstream>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/scoped_array.hpp>
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "server.h"
#include "util.h"
#include "scaler.h"
#include "image.h"
-#include "dcp_video_frame.h"
+#include "dcp_video.h"
#include "config.h"
#include "cross.h"
-#include "player_video_frame.h"
+#include "player_video.h"
+#include "encoded_data.h"
+ #include "safe_stringstream.h"
#include "i18n.h"
#define LOG_ERROR_NC(...) _log->log (__VA_ARGS__, Log::TYPE_ERROR);
using std::string;
- using std::stringstream;
using std::multimap;
using std::vector;
using std::list;
using boost::bind;
using boost::scoped_array;
using boost::optional;
-using libdcp::Size;
-using libdcp::raw_convert;
+using dcp::Size;
+using dcp::raw_convert;
Server::Server (shared_ptr<Log> log, bool verbose)
- : _log (log)
+ : _terminate (false)
+ , _log (log)
, _verbose (verbose)
+ , _acceptor (_io_service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), Config::instance()->server_port_base()))
{
}
+Server::~Server ()
+{
+ {
+ boost::mutex::scoped_lock lm (_worker_mutex);
+ _terminate = true;
+ _empty_condition.notify_all ();
+ }
+
+ for (vector<boost::thread*>::iterator i = _worker_threads.begin(); i != _worker_threads.end(); ++i) {
+ (*i)->join ();
+ delete *i;
+ }
+
+ _io_service.stop ();
+
+ _broadcast.io_service.stop ();
+ _broadcast.thread->join ();
+}
+
/** @param after_read Filled in with gettimeofday() after reading the input from the network.
* @param after_encode Filled in with gettimeofday() after encoding the image.
*/
scoped_array<char> buffer (new char[length]);
socket->read (reinterpret_cast<uint8_t*> (buffer.get()), length);
- stringstream s (buffer.get());
+ string s (buffer.get());
shared_ptr<cxml::Document> xml (new cxml::Document ("EncodingRequest"));
- xml->read_stream (s);
+ xml->read_string (s);
if (xml->number_child<int> ("Version") != SERVER_LINK_VERSION) {
cerr << "Mismatched server/client versions\n";
LOG_ERROR_NC ("Mismatched server/client versions");
return -1;
}
- shared_ptr<PlayerVideoFrame> pvf (new PlayerVideoFrame (xml, socket, _log));
+ shared_ptr<PlayerVideo> pvf (new PlayerVideo (xml, socket, _log));
- DCPVideoFrame dcp_video_frame (pvf, xml, _log);
+ DCPVideo dcp_video_frame (pvf, xml, _log);
gettimeofday (&after_read, 0);
{
while (true) {
boost::mutex::scoped_lock lock (_worker_mutex);
- while (_queue.empty ()) {
+ while (_queue.empty () && !_terminate) {
_empty_condition.wait (lock);
}
+ if (_terminate) {
+ return;
+ }
+
shared_ptr<Socket> socket = _queue.front ();
_queue.pop_front ();
struct timeval end;
gettimeofday (&end, 0);
- stringstream message;
+ SafeStringStream message;
message.precision (2);
message << fixed
<< "Encoded frame " << frame << " from " << ip << ": "
_broadcast.thread = new thread (bind (&Server::broadcast_thread, this));
- boost::asio::io_service io_service;
-
- boost::asio::ip::tcp::acceptor acceptor (
- io_service,
- boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), Config::instance()->server_port_base ())
- );
-
- while (true) {
- shared_ptr<Socket> socket (new Socket);
- acceptor.accept (socket->socket ());
-
- boost::mutex::scoped_lock lock (_worker_mutex);
-
- /* Wait until the queue has gone down a bit */
- while (int (_queue.size()) >= num_threads * 2) {
- _full_condition.wait (lock);
- }
-
- _queue.push_back (socket);
- _empty_condition.notify_all ();
- }
+ start_accept ();
+ _io_service.run ();
}
void
Server::broadcast_thread ()
try
{
- boost::asio::io_service io_service;
-
boost::asio::ip::address address = boost::asio::ip::address_v4::any ();
boost::asio::ip::udp::endpoint listen_endpoint (address, Config::instance()->server_port_base() + 1);
- _broadcast.socket = new boost::asio::ip::udp::socket (io_service);
+ _broadcast.socket = new boost::asio::ip::udp::socket (_broadcast.io_service);
_broadcast.socket->open (listen_endpoint.protocol ());
_broadcast.socket->bind (listen_endpoint);
boost::bind (&Server::broadcast_received, this)
);
- io_service.run ();
+ _broadcast.io_service.run ();
}
catch (...)
{
xmlpp::Document doc;
xmlpp::Element* root = doc.create_root_node ("ServerAvailable");
root->add_child("Threads")->add_child_text (raw_convert<string> (_worker_threads.size ()));
- stringstream xml;
- doc.write_to_stream (xml, "UTF-8");
+ string xml = doc.write_to_string ("UTF-8");
shared_ptr<Socket> socket (new Socket);
try {
socket->connect (boost::asio::ip::tcp::endpoint (_broadcast.send_endpoint.address(), Config::instance()->server_port_base() + 1));
- socket->write (xml.str().length() + 1);
- socket->write ((uint8_t *) xml.str().c_str(), xml.str().length() + 1);
+ socket->write (xml.length() + 1);
+ socket->write ((uint8_t *) xml.c_str(), xml.length() + 1);
} catch (...) {
}
_broadcast.send_endpoint, boost::bind (&Server::broadcast_received, this)
);
}
+
+void
+Server::start_accept ()
+{
+ if (_terminate) {
+ return;
+ }
+
+ shared_ptr<Socket> socket (new Socket);
+ _acceptor.async_accept (socket->socket (), boost::bind (&Server::handle_accept, this, socket, boost::asio::placeholders::error));
+}
+
+void
+Server::handle_accept (shared_ptr<Socket> socket, boost::system::error_code const & error)
+{
+ if (error) {
+ return;
+ }
+
+ boost::mutex::scoped_lock lock (_worker_mutex);
+
+ /* Wait until the queue has gone down a bit */
+ while (_queue.size() >= _worker_threads.size() * 2 && !_terminate) {
+ _full_condition.wait (lock);
+ }
+
+ _queue.push_back (socket);
+ _empty_condition.notify_all ();
+
+ start_accept ();
+}
+
*/
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "server_finder.h"
#include "exceptions.h"
#include "util.h"
#include "ui_signaller.h"
using std::string;
- using std::stringstream;
using std::list;
using std::vector;
using std::cout;
using boost::shared_ptr;
using boost::scoped_array;
-using libdcp::raw_convert;
+using dcp::raw_convert;
ServerFinder* ServerFinder::_instance = 0;
scoped_array<char> buffer (new char[length]);
sock->read (reinterpret_cast<uint8_t*> (buffer.get()), length);
- stringstream s (buffer.get());
+ string s (buffer.get());
shared_ptr<cxml::Document> xml (new cxml::Document ("ServerAvailable"));
- xml->read_stream (s);
+ xml->read_string (s);
string const ip = sock->socket().remote_endpoint().address().to_string ();
if (!server_found (ip)) {
*/
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "sndfile_content.h"
#include "sndfile_decoder.h"
#include "film.h"
#include "compose.hpp"
#include "job.h"
#include "util.h"
+ #include "safe_stringstream.h"
#include "i18n.h"
using std::string;
- using std::stringstream;
using std::cout;
using boost::shared_ptr;
-using libdcp::raw_convert;
+using dcp::raw_convert;
SndfileContent::SndfileContent (shared_ptr<const Film> f, boost::filesystem::path p)
: Content (f, p)
- , AudioContent (f, p)
- , _audio_channels (0)
- , _audio_length (0)
- , _audio_frame_rate (0)
+ , SingleStreamAudioContent (f, p)
{
}
-SndfileContent::SndfileContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
+SndfileContent::SndfileContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
: Content (f, node)
- , AudioContent (f, node)
- , _audio_mapping (node->node_child ("AudioMapping"), version)
+ , SingleStreamAudioContent (f, node, version)
{
- _audio_channels = node->number_child<int> ("AudioChannels");
- _audio_length = node->number_child<AudioContent::Frame> ("AudioLength");
- _audio_frame_rate = node->number_child<int> ("AudioFrameRate");
+
}
+void
+SndfileContent::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text ("Sndfile");
+ Content::as_xml (node);
+ SingleStreamAudioContent::as_xml (node);
+}
+
+
string
SndfileContent::summary () const
{
return "";
}
- stringstream s;
+ SafeStringStream s;
s << String::compose (
_("%1 channels, %2kHz, %3 samples"),
audio_channels(),
- content_audio_frame_rate() / 1000.0,
- audio_length()
+ audio_frame_rate() / 1000.0,
+ audio_length().frames (audio_frame_rate ())
);
return s.str ();
{
job->set_progress_unknown ();
Content::examine (job);
-
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- SndfileDecoder dec (film, shared_from_this());
-
- {
- boost::mutex::scoped_lock lm (_mutex);
- _audio_channels = dec.audio_channels ();
- _audio_length = dec.audio_length ();
- _audio_frame_rate = dec.audio_frame_rate ();
- }
-
- signal_changed (AudioContentProperty::AUDIO_CHANNELS);
- signal_changed (AudioContentProperty::AUDIO_LENGTH);
- signal_changed (AudioContentProperty::AUDIO_FRAME_RATE);
-
- {
- boost::mutex::scoped_lock lm (_mutex);
- /* XXX: do this in signal_changed...? */
- _audio_mapping = AudioMapping (_audio_channels);
- _audio_mapping.make_default ();
- }
-
- signal_changed (AudioContentProperty::AUDIO_MAPPING);
-}
-
-void
-SndfileContent::as_xml (xmlpp::Node* node) const
-{
- node->add_child("Type")->add_child_text ("Sndfile");
- Content::as_xml (node);
- AudioContent::as_xml (node);
-
- node->add_child("AudioChannels")->add_child_text (raw_convert<string> (audio_channels ()));
- node->add_child("AudioLength")->add_child_text (raw_convert<string> (audio_length ()));
- node->add_child("AudioFrameRate")->add_child_text (raw_convert<string> (content_audio_frame_rate ()));
- _audio_mapping.as_xml (node->add_child("AudioMapping"));
+ shared_ptr<AudioExaminer> dec (new SndfileDecoder (shared_from_this()));
+ take_from_audio_examiner (dec);
}
-Time
+DCPTime
SndfileContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateChange frc = film->active_frame_rate_change (position ());
-
- OutputAudioFrame const len = divide_with_round (
- audio_length() * output_audio_frame_rate() * frc.source,
- content_audio_frame_rate() * film->video_frame_rate()
- );
-
- return film->audio_frames_to_time (len);
+ return DCPTime (audio_length(), film->active_frame_rate_change (position ()));
}
-void
-SndfileContent::set_audio_mapping (AudioMapping m)
-{
- {
- boost::mutex::scoped_lock lm (_mutex);
- _audio_mapping = m;
- }
-
- signal_changed (AudioContentProperty::AUDIO_MAPPING);
-}
#include "film.h"
#include "transcoder.h"
#include "log.h"
+ #include "safe_stringstream.h"
#include "i18n.h"
#define LOG_ERROR_NC(...) _film->log()->log (__VA_ARGS__, Log::TYPE_ERROR);
using std::string;
- using std::stringstream;
using std::fixed;
using std::setprecision;
+using std::cout;
using boost::shared_ptr;
/** @param s Film to use.
return Job::status ();
}
- stringstream s;
+ SafeStringStream s;
s << Job::status ();
return s.str ();
}
+/** @return Approximate remaining time in seconds */
int
TranscodeJob::remaining_time () const
{
}
/* Compute approximate proposed length here, as it's only here that we need it */
- OutputVideoFrame const left = _film->time_to_video_frames (_film->length ()) - t->video_frames_out();
- return left / fps;
+ return (_film->length().frames (_film->video_frame_rate ()) - t->video_frames_out()) / fps;
}
*/
#include <string>
- #include <sstream>
#include <boost/algorithm/string.hpp>
#include <curl/curl.h>
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "update.h"
#include "version.h"
#include "ui_signaller.h"
+ #include "safe_stringstream.h"
#define BUFFER_SIZE 1024
using std::cout;
using std::min;
using std::string;
-using libdcp::raw_convert;
+using std::stringstream;
+using dcp::raw_convert;
+/** Singleton instance */
UpdateChecker* UpdateChecker::_instance = 0;
static size_t
return reinterpret_cast<UpdateChecker*>(user)->write_callback (data, size, nmemb);
}
+/** Construct an UpdateChecker. This sets things up and starts a thread to
+ * do the work.
+ */
UpdateChecker::UpdateChecker ()
: _buffer (new char[BUFFER_SIZE])
, _offset (0)
delete[] _buffer;
}
+/** Start running the update check */
void
UpdateChecker::run ()
{
UpdateChecker::thread ()
{
while (true) {
+ /* Block until there is something to do */
boost::mutex::scoped_lock lock (_process_mutex);
while (_to_do == 0) {
_condition.wait (lock);
try {
_offset = 0;
+
+ /* Perform the request */
int r = curl_easy_perform (_curl);
if (r != CURLE_OK) {
set_state (FAILED);
return;
}
+
+ /* Parse the reply */
_buffer[_offset] = '\0';
- stringstream s;
- s << _buffer;
+ string s (_buffer);
cxml::Document doc ("Update");
- doc.read_stream (s);
+ doc.read_string (s);
{
boost::mutex::scoped_lock lm (_data_mutex);
* @brief Some utility functions and classes.
*/
- #include <sstream>
#include <iomanip>
#include <iostream>
#include <fstream>
#endif
#include <glib.h>
#include <openjpeg.h>
+#include <pangomm/init.h>
#include <magick/MagickCore.h>
#include <magick/version.h>
-#include <libdcp/version.h>
-#include <libdcp/util.h>
-#include <libdcp/signer_chain.h>
-#include <libdcp/signer.h>
-#include <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>
#include "scaler.h"
#include "dcp_content_type.h"
#include "filter.h"
-#include "sound_processor.h"
+#include "cinema_sound_processor.h"
#include "config.h"
#include "ratio.h"
#include "job.h"
#include "cross.h"
#include "video_content.h"
+#include "rect.h"
#include "md5_digester.h"
+#include "audio_processor.h"
+ #include "safe_stringstream.h"
#ifdef DCPOMATIC_WINDOWS
#include "stack.hpp"
#endif
#include "i18n.h"
using std::string;
- using std::stringstream;
using std::setfill;
using std::ostream;
using std::endl;
using boost::shared_ptr;
using boost::thread;
using boost::optional;
-using libdcp::Size;
-using libdcp::raw_convert;
+using dcp::Size;
+using dcp::raw_convert;
static boost::thread::id ui_thread;
static boost::filesystem::path backtrace_file;
int h = m / 60;
m -= (h * 60);
- stringstream hms;
+ SafeStringStream hms;
hms << h << N_(":");
hms.width (2);
hms << std::setfill ('0') << m << N_(":");
int h = m / 60;
m -= (h * 60);
- stringstream ap;
+ SafeStringStream ap;
bool const hours = h > 0;
bool const minutes = h < 10 && m > 0;
static string
ffmpeg_version_to_string (int v)
{
- stringstream s;
+ SafeStringStream s;
s << ((v & 0xff0000) >> 16) << N_(".") << ((v & 0xff00) >> 8) << N_(".") << (v & 0xff);
return s.str ();
}
-/** Return a user-readable string summarising the versions of our dependencies */
-string
-dependency_version_summary ()
-{
- SafeStringStream s;
- s << N_("libopenjpeg ") << opj_version () << N_(", ")
- << N_("libavcodec ") << ffmpeg_version_to_string (avcodec_version()) << N_(", ")
- << N_("libavfilter ") << ffmpeg_version_to_string (avfilter_version()) << N_(", ")
- << N_("libavformat ") << ffmpeg_version_to_string (avformat_version()) << N_(", ")
- << N_("libavutil ") << ffmpeg_version_to_string (avutil_version()) << N_(", ")
- << N_("libswscale ") << ffmpeg_version_to_string (swscale_version()) << N_(", ")
- << MagickVersion << N_(", ")
- << N_("libssh ") << ssh_version (0) << N_(", ")
- << N_("libdcp ") << libdcp::version << N_(" git ") << libdcp::git_commit;
-
- return s.str ();
-}
-
double
seconds (struct timeval t)
{
set_terminate (terminate);
- libdcp::init ();
+ Pango::init ();
+ dcp::init ();
Ratio::setup_ratios ();
VideoContentScale::setup_scales ();
DCPContentType::setup_dcp_content_types ();
Scaler::setup_scalers ();
Filter::setup_filters ();
- SoundProcessor::setup_sound_processors ();
+ CinemaSoundProcessor::setup_cinema_sound_processors ();
+ AudioProcessor::setup_audio_processors ();
ui_thread = boost::this_thread::get_id ();
}
return a - (a % t);
}
+/** @param n A number.
+ * @param r Rounding `boundary' (must be a power of 2)
+ * @return n rounded to the nearest r
+ */
+int
+round_to (float n, int r)
+{
+ assert (r == 1 || r == 2 || r == 4);
+ return int (n + float(r) / 2) &~ (r - 1);
+}
+
/** Read a sequence of key / value pairs from a text stream;
* the keys are the first words on the line, and the values are
* the remainder of the line following the key. Lines beginning
assert (boost::this_thread::get_id() == ui_thread);
}
-/** @param v Content video frame.
- * @param audio_sample_rate Source audio sample rate.
- * @param frames_per_second Number of video frames per second.
- * @return Equivalent number of audio frames for `v'.
- */
-int64_t
-video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second)
-{
- return ((int64_t) v * audio_sample_rate / frames_per_second);
-}
-
string
audio_channel_name (int c)
{
return t;
}
-shared_ptr<const libdcp::Signer>
-make_signer ()
-{
- boost::filesystem::path const sd = Config::instance()->signer_chain_directory ();
-
- /* Remake the chain if any of it is missing */
-
- list<boost::filesystem::path> files;
- files.push_back ("ca.self-signed.pem");
- files.push_back ("intermediate.signed.pem");
- files.push_back ("leaf.signed.pem");
- files.push_back ("leaf.key");
-
- list<boost::filesystem::path>::const_iterator i = files.begin();
- while (i != files.end()) {
- boost::filesystem::path p (sd);
- p /= *i;
- if (!boost::filesystem::exists (p)) {
- boost::filesystem::remove_all (sd);
- boost::filesystem::create_directories (sd);
- libdcp::make_signer_chain (sd, openssl_path ());
- break;
- }
-
- ++i;
- }
-
- libdcp::CertificateChain chain;
-
- {
- boost::filesystem::path p (sd);
- p /= "ca.self-signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
- }
-
- {
- boost::filesystem::path p (sd);
- p /= "intermediate.signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
- }
-
- {
- boost::filesystem::path p (sd);
- p /= "leaf.signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
- }
-
- boost::filesystem::path signer_key (sd);
- signer_key /= "leaf.key";
-
- return shared_ptr<const libdcp::Signer> (new libdcp::Signer (chain, signer_key));
-}
-
map<string, string>
split_get_request (string url)
{
return r;
}
-libdcp::Size
-fit_ratio_within (float ratio, libdcp::Size full_frame)
+dcp::Size
+fit_ratio_within (float ratio, dcp::Size full_frame, int round)
{
if (ratio < full_frame.ratio ()) {
- return libdcp::Size (rint (full_frame.height * ratio), full_frame.height);
+ return dcp::Size (round_to (full_frame.height * ratio, round), full_frame.height);
}
- return libdcp::Size (full_frame.width, rint (full_frame.width / ratio));
+ return dcp::Size (full_frame.width, round_to (full_frame.width / ratio, round));
}
void *
}
}
- stringstream s;
+/** Return a user-readable string summarising the versions of our dependencies */
+string
+dependency_version_summary ()
+{
++ SafeStringStream s;
+ s << N_("libopenjpeg ") << opj_version () << N_(", ")
+ << N_("libavcodec ") << ffmpeg_version_to_string (avcodec_version()) << N_(", ")
+ << N_("libavfilter ") << ffmpeg_version_to_string (avfilter_version()) << N_(", ")
+ << N_("libavformat ") << ffmpeg_version_to_string (avformat_version()) << N_(", ")
+ << N_("libavutil ") << ffmpeg_version_to_string (avutil_version()) << N_(", ")
+ << N_("libswscale ") << ffmpeg_version_to_string (swscale_version()) << N_(", ")
+ << MagickVersion << N_(", ")
+ << N_("libssh ") << ssh_version (0) << N_(", ")
+ << N_("libdcp ") << dcp::version << N_(" git ") << dcp::git_commit;
+
+ return s.str ();
+}
+
+/** Construct a ScopedTemporary. A temporary filename is decided but the file is not opened
+ * until ::open() is called.
+ */
ScopedTemporary::ScopedTemporary ()
: _open (0)
{
_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path ();
}
+/** Close and delete the temporary file */
ScopedTemporary::~ScopedTemporary ()
{
close ();
boost::filesystem::remove (_file, ec);
}
+/** @return temporary filename */
char const *
ScopedTemporary::c_str () const
{
return _file.string().c_str ();
}
+/** Open the temporary file.
+ * @return File's FILE pointer.
+ */
FILE*
ScopedTemporary::open (char const * params)
{
return _open;
}
+/** Close the file */
void
ScopedTemporary::close ()
{
_open = 0;
}
}
+
+ContentTimePeriod
+subtitle_period (AVSubtitle const & sub)
+{
+ ContentTime const packet_time = ContentTime::from_seconds (static_cast<double> (sub.pts) / AV_TIME_BASE);
+
+ ContentTimePeriod period (
+ packet_time + ContentTime::from_seconds (sub.start_display_time / 1e3),
+ packet_time + ContentTime::from_seconds (sub.end_display_time / 1e3)
+ );
+
+ return period;
+}
#include <iomanip>
#include <libcxml/cxml.h>
-#include <libdcp/colour_matrix.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/colour_matrix.h>
+#include <dcp/raw_convert.h>
#include "video_content.h"
#include "video_examiner.h"
#include "compose.hpp"
#include "film.h"
#include "exceptions.h"
#include "frame_rate_change.h"
+#include "log.h"
+ #include "safe_stringstream.h"
#include "i18n.h"
+#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+
int const VideoContentProperty::VIDEO_SIZE = 0;
int const VideoContentProperty::VIDEO_FRAME_RATE = 1;
int const VideoContentProperty::VIDEO_FRAME_TYPE = 2;
int const VideoContentProperty::COLOUR_CONVERSION = 5;
using std::string;
- using std::stringstream;
using std::setprecision;
using std::cout;
using std::vector;
using boost::shared_ptr;
using boost::optional;
using boost::dynamic_pointer_cast;
-using libdcp::raw_convert;
+using dcp::raw_convert;
vector<VideoContentScale> VideoContentScale::_scales;
VideoContent::VideoContent (shared_ptr<const Film> f)
: Content (f)
, _video_length (0)
- , _original_video_frame_rate (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
, _scale (Config::instance()->default_scale ())
setup_default_colour_conversion ();
}
-VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
+VideoContent::VideoContent (shared_ptr<const Film> f, DCPTime s, ContentTime len)
: Content (f, s)
, _video_length (len)
- , _original_video_frame_rate (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
, _scale (Config::instance()->default_scale ())
VideoContent::VideoContent (shared_ptr<const Film> f, boost::filesystem::path p)
: Content (f, p)
, _video_length (0)
- , _original_video_frame_rate (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
, _scale (Config::instance()->default_scale ())
setup_default_colour_conversion ();
}
-VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
+VideoContent::VideoContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
: Content (f, node)
{
- _video_length = node->number_child<VideoContent::Frame> ("VideoLength");
_video_size.width = node->number_child<int> ("VideoWidth");
_video_size.height = node->number_child<int> ("VideoHeight");
_video_frame_rate = node->number_child<float> ("VideoFrameRate");
- _original_video_frame_rate = node->optional_number_child<float> ("OriginalVideoFrameRate").get_value_or (_video_frame_rate);
+
+ if (version < 32) {
+ /* DCP-o-matic 1.0 branch */
+ _video_length = ContentTime::from_frames (node->number_child<int64_t> ("VideoLength"), _video_frame_rate);
+ } else {
+ _video_length = ContentTime (node->number_child<ContentTime::Type> ("VideoLength"));
+ }
+
_video_frame_type = static_cast<VideoFrameType> (node->number_child<int> ("VideoFrameType"));
_crop.left = node->number_child<int> ("LeftCrop");
_crop.right = node->number_child<int> ("RightCrop");
}
_video_size = ref->video_size ();
- _original_video_frame_rate = ref->original_video_frame_rate ();
_video_frame_rate = ref->video_frame_rate ();
_video_frame_type = ref->video_frame_type ();
_crop = ref->crop ();
VideoContent::as_xml (xmlpp::Node* node) const
{
boost::mutex::scoped_lock lm (_mutex);
- node->add_child("VideoLength")->add_child_text (raw_convert<string> (_video_length));
+ node->add_child("VideoLength")->add_child_text (raw_convert<string> (_video_length.get ()));
node->add_child("VideoWidth")->add_child_text (raw_convert<string> (_video_size.width));
node->add_child("VideoHeight")->add_child_text (raw_convert<string> (_video_size.height));
node->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
- node->add_child("OriginalVideoFrameRate")->add_child_text (raw_convert<string> (_original_video_frame_rate));
node->add_child("VideoFrameType")->add_child_text (raw_convert<string> (static_cast<int> (_video_frame_type)));
_crop.as_xml (node);
_scale.as_xml (node->add_child("Scale"));
void
VideoContent::setup_default_colour_conversion ()
{
- _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
+ _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
}
void
VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
{
/* These examiner calls could call other content methods which take a lock on the mutex */
- libdcp::Size const vs = d->video_size ();
+ dcp::Size const vs = d->video_size ();
float const vfr = d->video_frame_rate ();
-
+ ContentTime vl = d->video_length ();
+
{
boost::mutex::scoped_lock lm (_mutex);
_video_size = vs;
_video_frame_rate = vfr;
- _original_video_frame_rate = vfr;
+ _video_length = vl;
}
+
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+ LOG_GENERAL ("Video length obtained from header as %1 frames", _video_length.frames (_video_frame_rate));
signal_changed (VideoContentProperty::VIDEO_SIZE);
signal_changed (VideoContentProperty::VIDEO_FRAME_RATE);
+ signal_changed (ContentProperty::LENGTH);
}
return "";
}
- stringstream s;
+ SafeStringStream s;
s << String::compose (
_("%1x%2 pixels (%3:1)"),
string
VideoContent::identifier () const
{
- stringstream s;
+ SafeStringStream s;
s << Content::identifier()
<< "_" << crop().left
<< "_" << crop().right
{
return String::compose (
"video: length %1, size %2x%3, rate %4",
- video_length_after_3d_combine(), video_size().width, video_size().height, video_frame_rate()
+ video_length_after_3d_combine().seconds(),
+ video_size().width,
+ video_size().height,
+ video_frame_rate()
);
}
-libdcp::Size
+dcp::Size
VideoContent::video_size_after_3d_split () const
{
- libdcp::Size const s = video_size ();
+ dcp::Size const s = video_size ();
switch (video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
case VIDEO_FRAME_TYPE_3D_ALTERNATE:
case VIDEO_FRAME_TYPE_3D_RIGHT:
return s;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
- return libdcp::Size (s.width / 2, s.height);
+ return dcp::Size (s.width / 2, s.height);
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
- return libdcp::Size (s.width, s.height / 2);
+ return dcp::Size (s.width, s.height / 2);
}
assert (false);
}
/** @return Video size after 3D split and crop */
-libdcp::Size
+dcp::Size
VideoContent::video_size_after_crop () const
{
return crop().apply (video_size_after_3d_split ());
}
/** @param t A time offset from the start of this piece of content.
- * @return Corresponding frame index.
+ * @return Corresponding time with respect to the content.
*/
-VideoContent::Frame
-VideoContent::time_to_content_video_frames (Time t) const
+ContentTime
+VideoContent::dcp_time_to_content_time (DCPTime t) const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateChange frc (video_frame_rate(), film->video_frame_rate());
-
- /* Here we are converting from time (in the DCP) to a frame number in the content.
- Hence we need to use the DCP's frame rate and the double/skip correction, not
- the source's rate.
- */
- return t * film->video_frame_rate() / (frc.factor() * TIME_HZ);
+ return ContentTime (t, FrameRateChange (video_frame_rate(), film->video_frame_rate()));
}
void
}
-VideoContentScale::VideoContentScale (shared_ptr<cxml::Node> node)
+VideoContentScale::VideoContentScale (cxml::NodePtr node)
: _ratio (0)
, _scale (true)
{
string
VideoContentScale::id () const
{
- stringstream s;
+ SafeStringStream s;
if (_ratio) {
s << _ratio->id () << "_";
/** @param display_container Size of the container that we are displaying this content in.
* @param film_container The size of the film's image.
*/
-libdcp::Size
-VideoContentScale::size (shared_ptr<const VideoContent> c, libdcp::Size display_container, libdcp::Size film_container) const
+dcp::Size
+VideoContentScale::size (shared_ptr<const VideoContent> c, dcp::Size display_container, dcp::Size film_container, int round) const
{
if (_ratio) {
- return fit_ratio_within (_ratio->ratio (), display_container);
+ return fit_ratio_within (_ratio->ratio (), display_container, round);
}
- libdcp::Size const ac = c->video_size_after_crop ();
+ dcp::Size const ac = c->video_size_after_crop ();
/* Force scale if the film_container is smaller than the content's image */
if (_scale || film_container.width < ac.width || film_container.height < ac.height) {
- return fit_ratio_within (ac.ratio (), display_container);
+ return fit_ratio_within (ac.ratio (), display_container, 1);
}
/* Scale the image so that it will be in the right place in film_container, even if display_container is a
different size.
*/
- return libdcp::Size (
- c->video_size().width * float(display_container.width) / film_container.width,
- c->video_size().height * float(display_container.height) / film_container.height
+ return dcp::Size (
+ round_to (c->video_size().width * float(display_container.width) / film_container.width, round),
+ round_to (c->video_size().height * float(display_container.height) / film_container.height, round)
);
}
audio_buffers.cc
audio_content.cc
audio_decoder.cc
+ audio_filter.cc
audio_mapping.cc
+ audio_processor.cc
cinema.cc
+ cinema_sound_processor.cc
colour_conversion.cc
config.cc
content.cc
content_factory.cc
+ content_subtitle.cc
cross.cc
+ dcp_content.cc
dcp_content_type.cc
- dcp_video_frame.cc
- decoder.cc
+ dcp_decoder.cc
+ dcp_examiner.cc
+ dcp_subtitle_content.cc
+ dcp_subtitle_decoder.cc
+ dcp_video.cc
+ dcpomatic_time.cc
dolby_cp750.cc
encoder.cc
+ encoded_data.cc
examine_content_job.cc
exceptions.cc
file_group.cc
filter_graph.cc
ffmpeg.cc
+ ffmpeg_audio_stream.cc
ffmpeg_content.cc
ffmpeg_decoder.cc
ffmpeg_examiner.cc
+ ffmpeg_stream.cc
+ ffmpeg_subtitle_stream.cc
film.cc
filter.cc
frame_rate_change.cc
image_examiner.cc
image_proxy.cc
isdcf_metadata.cc
+ j2k_image_proxy.cc
job.cc
job_manager.cc
kdm.cc
log.cc
+ magick_image_proxy.cc
md5_digester.cc
- piece.cc
+ mid_side_decoder.cc
player.cc
- player_video_frame.cc
+ player_video.cc
playlist.cc
ratio.cc
+ raw_image_proxy.cc
+ render_subtitles.cc
resampler.cc
+ safe_stringstream.cc
scp_dcp_job.cc
scaler.cc
send_kdm_email_job.cc
server.cc
server_finder.cc
+ single_stream_audio_content.cc
sndfile_content.cc
sndfile_decoder.cc
- sound_processor.cc
- subtitle.cc
+ subrip.cc
+ subrip_content.cc
+ subrip_decoder.cc
subtitle_content.cc
subtitle_decoder.cc
timer.cc
types.cc
ui_signaller.cc
update.cc
+ upmixer_a.cc
util.cc
video_content.cc
video_decoder.cc
else:
obj = bld(features = 'cxx cxxshlib')
- obj.name = 'libdcpomatic'
+ obj.name = 'libdcpomatic2'
obj.export_includes = ['..']
obj.uselib = """
AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE
BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2
- SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XMLPP
- CURL ZIP QUICKMAIL XMLSEC
+ SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XML++
+ CURL ZIP QUICKMAIL PANGOMM CAIROMM XMLSEC
"""
if bld.env.TARGET_OSX:
if bld.env.BUILD_STATIC:
obj.uselib += ' XMLPP'
- obj.target = 'dcpomatic'
+ obj.target = 'dcpomatic2'
- i18n.po_to_mo(os.path.join('src', 'lib'), 'libdcpomatic', bld)
+ i18n.po_to_mo(os.path.join('src', 'lib'), 'libdcpomatic2', bld)
def pot(bld):
i18n.pot(os.path.join('src', 'lib'), sources, 'libdcpomatic')
#include <wx/stdpaths.h>
#include <wx/cmdline.h>
#include <wx/preferences.h>
-#include <libdcp/exceptions.h>
+#include <dcp/exceptions.h>
#include "wx/film_viewer.h"
#include "wx/film_editor.h"
#include "wx/job_manager_view.h"
#include "wx/servers_list_dialog.h"
#include "wx/hints_dialog.h"
#include "wx/update_dialog.h"
+#include "wx/content_panel.h"
#include "lib/film.h"
#include "lib/config.h"
#include "lib/util.h"
using std::cout;
using std::string;
using std::wstring;
- using std::stringstream;
using std::map;
using std::make_pair;
using std::list;
shared_ptr<Job> (new SendKDMEmailJob (film, d->screens (), d->cpl (), d->from (), d->until (), d->formulation ()))
);
}
- } catch (libdcp::NotEncryptedError& e) {
+ } catch (dcp::NotEncryptedError& e) {
error_dialog (this, _("CPL's content is not encrypted."));
} catch (exception& e) {
error_dialog (this, e.what ());
void content_scale_to_fit_width ()
{
- VideoContentList vc = _film_editor->selected_video_content ();
+ VideoContentList vc = _film_editor->content_panel()->selected_video ();
for (VideoContentList::iterator i = vc.begin(); i != vc.end(); ++i) {
(*i)->scale_and_crop_to_fit_width ();
}
void content_scale_to_fit_height ()
{
- VideoContentList vc = _film_editor->selected_video_content ();
+ VideoContentList vc = _film_editor->content_panel()->selected_video ();
for (VideoContentList::iterator i = vc.begin(); i != vc.end(); ++i) {
(*i)->scale_and_crop_to_fit_height ();
}
}
bool const dcp_creation = (i != jobs.end ()) && !(*i)->finished ();
bool const have_cpl = film && !film->cpls().empty ();
- bool const have_selected_video_content = !_film_editor->selected_video_content().empty();
+ bool const have_selected_video_content = !_film_editor->content_panel()->selected_video().empty();
for (map<wxMenuItem*, int>::iterator j = menu_items.begin(); j != menu_items.end(); ++j) {
{ wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 }
};
+/** @class App
+ * @brief The magic App class for wxWidgets.
+ */
class App : public wxApp
{
bool OnInit ()
*/
#include <getopt.h>
-#include <libdcp/certificates.h>
+#include <dcp/certificates.h>
#include "lib/film.h"
#include "lib/cinema.h"
#include "lib/kdm.h"
#include "lib/config.h"
#include "lib/exceptions.h"
+ #include "lib/safe_stringstream.h"
using std::string;
- using std::stringstream;
using std::cout;
using std::cerr;
using std::list;
cerr << "Syntax: " << program_name << " [OPTION] [<FILM>]\n"
" -h, --help show this help\n"
" -o, --output output file or directory\n"
- " -f, --valid-from valid from time (e.g. \"2013-09-28 01:41:51\") or \"now\"\n"
- " -t, --valid-to valid to time (e.g. \"2014-09-28 01:41:51\")\n"
+ " -f, --valid-from valid from time (in local time zone) (e.g. \"2013-09-28 01:41:51\") or \"now\"\n"
+ " -t, --valid-to valid to time (in local time zone) (e.g. \"2014-09-28 01:41:51\")\n"
" -d, --valid-duration valid duration (e.g. \"1 day\", \"4 hours\", \"2 weeks\")\n"
" --formulation modified-transitional-1, dci-any or dci-specific [default modified-transitional-1]\n"
" -z, --zip ZIP each cinema's KDMs into its own file\n"
static boost::posix_time::time_duration
duration_from_string (string d)
{
- stringstream s (d);
+ SafeStringStream s (d);
int N;
string unit;
s >> N >> unit;
bool cinemas = false;
string duration_string;
bool verbose = false;
- libdcp::KDM::Formulation formulation = libdcp::KDM::MODIFIED_TRANSITIONAL_1;
+ dcp::Formulation formulation = dcp::MODIFIED_TRANSITIONAL_1;
program_name = argv[0];
break;
case 'C':
if (string (optarg) == "modified-transitional-1") {
- formulation = libdcp::KDM::MODIFIED_TRANSITIONAL_1;
+ formulation = dcp::MODIFIED_TRANSITIONAL_1;
} else if (string (optarg) == "dci-any") {
- formulation = libdcp::KDM::DCI_ANY;
+ formulation = dcp::DCI_ANY;
} else if (string (optarg) == "dci-specific") {
- formulation = libdcp::KDM::DCI_SPECIFIC;
+ formulation = dcp::DCI_SPECIFIC;
} else {
- error ("unrecognised KDM formulation " + formulation);
+ error ("unrecognised KDM formulation " + string (optarg));
}
}
}
error ("you must specify --output");
}
- shared_ptr<libdcp::Certificate> certificate (new libdcp::Certificate (boost::filesystem::path (certificate_file)));
- libdcp::KDM kdm = film->make_kdm (certificate, cpl, valid_from.get(), valid_to.get(), formulation);
+ dcp::Certificate certificate (dcp::file_to_string (certificate_file));
+ dcp::EncryptedKDM kdm = film->make_kdm (certificate, cpl, valid_from.get(), valid_to.get(), formulation);
kdm.as_xml (output);
if (verbose) {
cout << "Generated KDM " << output << " for certificate.\n";
try {
if (zip) {
- write_kdm_zip_files (film, (*i)->screens(), cpl, valid_from.get(), valid_to.get(), formulation, output);
+ write_kdm_zip_files (
+ film, (*i)->screens(), cpl, dcp::LocalTime (valid_from.get()), dcp::LocalTime (valid_to.get()), formulation, output
+ );
+
if (verbose) {
cout << "Wrote ZIP files to " << output << "\n";
}
} else {
- write_kdm_files (film, (*i)->screens(), cpl, valid_from.get(), valid_to.get(), formulation, output);
+ write_kdm_files (
+ film, (*i)->screens(), cpl, dcp::LocalTime (valid_from.get()), dcp::LocalTime (valid_to.get()), formulation, output
+ );
+
if (verbose) {
cout << "Wrote KDM files to " << output << "\n";
}
#include "lib/server.h"
#include <iostream>
#include <stdexcept>
- #include <sstream>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include "lib/config.h"
-#include "lib/dcp_video_frame.h"
+#include "lib/dcp_video.h"
#include "lib/exceptions.h"
#include "lib/util.h"
#include "lib/config.h"
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ 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
*/
+/** @file src/wx/about_dialog.cc
+ * @brief The "about DCP-o-matic" dialogue box.
+ */
+
#include <wx/notebook.h>
#include <wx/hyperlink.h>
#include "lib/version.h"
wxArrayString supported_by;
supported_by.Add (wxT ("Manual AC"));
supported_by.Add (wxT ("Kambiz Afshar"));
+ supported_by.Add (wxT ("Alex Asp"));
supported_by.Add (wxT ("Louis Belloisy"));
supported_by.Add (wxT ("Mike Blakesley"));
supported_by.Add (wxT ("Jeff Boot"));
SetSizerAndFit (overall_sizer);
}
+/** Add a section of credits.
+ * @param name Name of section.
+ * @param credits List of names.
+ */
void
AboutDialog::add_section (wxString name, wxArrayString credits)
{
#include "lib/ffmpeg_content.h"
#include "lib/sndfile_content.h"
#include "lib/dcp_content_type.h"
-#include "lib/sound_processor.h"
#include "lib/scaler.h"
#include "lib/playlist.h"
#include "lib/content.h"
#include "lib/content_factory.h"
+#include "lib/dcp_content.h"
+ #include "lib/safe_stringstream.h"
#include "timecode.h"
#include "wx_util.h"
#include "film_editor.h"
-#include "isdcf_metadata_dialog.h"
#include "timeline_dialog.h"
#include "timing_panel.h"
#include "subtitle_panel.h"
#include "audio_panel.h"
#include "video_panel.h"
+#include "content_panel.h"
+#include "dcp_panel.h"
using std::string;
using std::cout;
- using std::stringstream;
using std::pair;
using std::fixed;
using std::setprecision;
/** @param f Film to edit */
FilmEditor::FilmEditor (shared_ptr<Film> f, wxWindow* parent)
: wxPanel (parent)
- , _menu (this)
- , _generally_sensitive (true)
- , _timeline_dialog (0)
{
wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
_main_notebook = new wxNotebook (this, wxID_ANY);
s->Add (_main_notebook, 1);
- make_content_panel ();
- _main_notebook->AddPage (_content_panel, _("Content"), true);
- make_dcp_panel ();
- _main_notebook->AddPage (_dcp_panel, _("DCP"), false);
+ _content_panel = new ContentPanel (_main_notebook, _film);
+ _main_notebook->AddPage (_content_panel->panel (), _("Content"), true);
+ _dcp_panel = new DCPPanel (_main_notebook, _film);
+ _main_notebook->AddPage (_dcp_panel->panel (), _("DCP"), false);
set_film (f);
- connect_to_widgets ();
JobManager::instance()->ActiveJobsChanged.connect (
bind (&FilmEditor::active_jobs_changed, this, _1)
);
- Config::instance()->Changed.connect (boost::bind (&FilmEditor::config_changed, this));
-
SetSizerAndFit (s);
}
-void
-FilmEditor::make_dcp_panel ()
-{
- _dcp_panel = new wxPanel (_main_notebook);
- _dcp_sizer = new wxBoxSizer (wxVERTICAL);
- _dcp_panel->SetSizer (_dcp_sizer);
-
- wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
- _dcp_sizer->Add (grid, 0, wxEXPAND | wxALL, 8);
-
- int r = 0;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Name"), true, wxGBPosition (r, 0));
- _name = new wxTextCtrl (_dcp_panel, wxID_ANY);
- grid->Add (_name, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND | wxLEFT | wxRIGHT);
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("DCP Name"), true, wxGBPosition (r, 0));
- _dcp_name = new wxStaticText (_dcp_panel, wxID_ANY, wxT (""));
- grid->Add (_dcp_name, wxGBPosition(r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
- ++r;
-
- int flags = wxALIGN_CENTER_VERTICAL;
-#ifdef __WXOSX__
- flags |= wxALIGN_RIGHT;
-#endif
-
- _use_isdcf_name = new wxCheckBox (_dcp_panel, wxID_ANY, _("Use ISDCF name"));
- grid->Add (_use_isdcf_name, wxGBPosition (r, 0), wxDefaultSpan, flags);
- _edit_isdcf_button = new wxButton (_dcp_panel, wxID_ANY, _("Details..."));
- grid->Add (_edit_isdcf_button, wxGBPosition (r, 1), wxDefaultSpan);
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Container"), true, wxGBPosition (r, 0));
- _container = new wxChoice (_dcp_panel, wxID_ANY);
- grid->Add (_container, wxGBPosition (r, 1), wxDefaultSpan, wxEXPAND);
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Content Type"), true, wxGBPosition (r, 0));
- _dcp_content_type = new wxChoice (_dcp_panel, wxID_ANY);
- grid->Add (_dcp_content_type, wxGBPosition (r, 1));
- ++r;
-
- {
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Frame Rate"), true, wxGBPosition (r, 0));
- _frame_rate_sizer = new wxBoxSizer (wxHORIZONTAL);
- _frame_rate_choice = new wxChoice (_dcp_panel, wxID_ANY);
- _frame_rate_sizer->Add (_frame_rate_choice, 1, wxALIGN_CENTER_VERTICAL);
- _frame_rate_spin = new wxSpinCtrl (_dcp_panel, wxID_ANY);
- _frame_rate_sizer->Add (_frame_rate_spin, 1, wxALIGN_CENTER_VERTICAL);
- setup_frame_rate_widget ();
- _best_frame_rate = new wxButton (_dcp_panel, wxID_ANY, _("Use best"));
- _frame_rate_sizer->Add (_best_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
- grid->Add (_frame_rate_sizer, wxGBPosition (r, 1));
- }
- ++r;
-
- _signed = new wxCheckBox (_dcp_panel, wxID_ANY, _("Signed"));
- grid->Add (_signed, wxGBPosition (r, 0), wxGBSpan (1, 2));
- ++r;
-
- _encrypted = new wxCheckBox (_dcp_panel, wxID_ANY, _("Encrypted"));
- grid->Add (_encrypted, wxGBPosition (r, 0), wxGBSpan (1, 2));
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Audio channels"), true, wxGBPosition (r, 0));
- _audio_channels = new wxSpinCtrl (_dcp_panel, wxID_ANY);
- grid->Add (_audio_channels, wxGBPosition (r, 1));
- ++r;
-
- _three_d = new wxCheckBox (_dcp_panel, wxID_ANY, _("3D"));
- grid->Add (_three_d, wxGBPosition (r, 0), wxGBSpan (1, 2));
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Resolution"), true, wxGBPosition (r, 0));
- _resolution = new wxChoice (_dcp_panel, wxID_ANY);
- grid->Add (_resolution, wxGBPosition (r, 1));
- ++r;
-
- {
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("JPEG2000 bandwidth"), true, wxGBPosition (r, 0));
- wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _j2k_bandwidth = new wxSpinCtrl (_dcp_panel, wxID_ANY);
- s->Add (_j2k_bandwidth, 1);
- add_label_to_sizer (s, _dcp_panel, _("Mbit/s"), false);
- grid->Add (s, wxGBPosition (r, 1));
- }
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Standard"), true, wxGBPosition (r, 0));
- _standard = new wxChoice (_dcp_panel, wxID_ANY);
- grid->Add (_standard, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Scaler"), true, wxGBPosition (r, 0));
- _scaler = new wxChoice (_dcp_panel, wxID_ANY);
- grid->Add (_scaler, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
- ++r;
-
- vector<Scaler const *> const sc = Scaler::all ();
- for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) {
- _scaler->Append (std_to_wx ((*i)->name()));
- }
-
- vector<Ratio const *> const ratio = Ratio::all ();
- for (vector<Ratio const *>::const_iterator i = ratio.begin(); i != ratio.end(); ++i) {
- _container->Append (std_to_wx ((*i)->nickname ()));
- }
-
- vector<DCPContentType const *> const ct = DCPContentType::all ();
- for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) {
- _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ()));
- }
-
- list<int> const dfr = Config::instance()->allowed_dcp_frame_rates ();
- for (list<int>::const_iterator i = dfr.begin(); i != dfr.end(); ++i) {
- _frame_rate_choice->Append (std_to_wx (boost::lexical_cast<string> (*i)));
- }
-
- _audio_channels->SetRange (0, MAX_DCP_AUDIO_CHANNELS);
- _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
- _frame_rate_spin->SetRange (1, 480);
-
- _resolution->Append (_("2K"));
- _resolution->Append (_("4K"));
-
- _standard->Append (_("SMPTE"));
- _standard->Append (_("Interop"));
-}
-
-void
-FilmEditor::connect_to_widgets ()
-{
- _name->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&FilmEditor::name_changed, this));
- _use_isdcf_name->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::use_isdcf_name_toggled, this));
- _edit_isdcf_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::edit_isdcf_button_clicked, this));
- _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::container_changed, this));
- _content->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&FilmEditor::content_selection_changed, this));
- _content->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&FilmEditor::content_selection_changed, this));
- _content->Bind (wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, boost::bind (&FilmEditor::content_right_click, this, _1));
- _content_add_file->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_add_file_clicked, this));
- _content_add_folder->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_add_folder_clicked, this));
- _content_remove->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_remove_clicked, this));
- _content_earlier->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_earlier_clicked, this));
- _content_later->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_later_clicked, this));
- _content_timeline->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_timeline_clicked, this));
- _scaler->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::scaler_changed, this));
- _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::dcp_content_type_changed, this));
- _frame_rate_choice->Bind(wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::frame_rate_choice_changed, this));
- _frame_rate_spin->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&FilmEditor::frame_rate_spin_changed, this));
- _best_frame_rate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::best_frame_rate_clicked, this));
- _signed->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::signed_toggled, this));
- _encrypted->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::encrypted_toggled, this));
- _audio_channels->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&FilmEditor::audio_channels_changed, this));
- _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&FilmEditor::j2k_bandwidth_changed, this));
- _resolution->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::resolution_changed, this));
- _sequence_video->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::sequence_video_changed, this));
- _three_d->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::three_d_changed, this));
- _standard->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::standard_changed, this));
-}
-
-void
-FilmEditor::make_content_panel ()
-{
- _content_panel = new wxPanel (_main_notebook);
- _content_sizer = new wxBoxSizer (wxVERTICAL);
- _content_panel->SetSizer (_content_sizer);
-
- {
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-
- _content = new wxListCtrl (_content_panel, wxID_ANY, wxDefaultPosition, wxSize (320, 160), wxLC_REPORT | wxLC_NO_HEADER);
- s->Add (_content, 1, wxEXPAND | wxTOP | wxBOTTOM, 6);
-
- _content->InsertColumn (0, wxT(""));
- _content->SetColumnWidth (0, 512);
-
- wxBoxSizer* b = new wxBoxSizer (wxVERTICAL);
- _content_add_file = new wxButton (_content_panel, wxID_ANY, _("Add file(s)..."));
- b->Add (_content_add_file, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
- _content_add_folder = new wxButton (_content_panel, wxID_ANY, _("Add folder..."));
- b->Add (_content_add_folder, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
- _content_remove = new wxButton (_content_panel, wxID_ANY, _("Remove"));
- b->Add (_content_remove, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
- _content_earlier = new wxButton (_content_panel, wxID_ANY, _("Up"));
- b->Add (_content_earlier, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
- _content_later = new wxButton (_content_panel, wxID_ANY, _("Down"));
- b->Add (_content_later, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
- _content_timeline = new wxButton (_content_panel, wxID_ANY, _("Timeline..."));
- b->Add (_content_timeline, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
-
- s->Add (b, 0, wxALL, 4);
-
- _content_sizer->Add (s, 0, wxEXPAND | wxALL, 6);
- }
-
- _sequence_video = new wxCheckBox (_content_panel, wxID_ANY, _("Keep video in sequence"));
- _content_sizer->Add (_sequence_video);
-
- _content_notebook = new wxNotebook (_content_panel, wxID_ANY);
- _content_sizer->Add (_content_notebook, 1, wxEXPAND | wxTOP, 6);
-
- _video_panel = new VideoPanel (this);
- _panels.push_back (_video_panel);
- _audio_panel = new AudioPanel (this);
- _panels.push_back (_audio_panel);
- _subtitle_panel = new SubtitlePanel (this);
- _panels.push_back (_subtitle_panel);
- _timing_panel = new TimingPanel (this);
- _panels.push_back (_timing_panel);
-}
-
-/** Called when the name widget has been changed */
-void
-FilmEditor::name_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_name (string (_name->GetValue().mb_str()));
-}
-
-void
-FilmEditor::j2k_bandwidth_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
-}
-
-void
-FilmEditor::signed_toggled ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_signed (_signed->GetValue ());
-}
-
-void
-FilmEditor::encrypted_toggled ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_encrypted (_encrypted->GetValue ());
-}
-
-/** Called when the frame rate choice widget has been changed */
-void
-FilmEditor::frame_rate_choice_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_video_frame_rate (
- boost::lexical_cast<int> (
- wx_to_std (_frame_rate_choice->GetString (_frame_rate_choice->GetSelection ()))
- )
- );
-}
-
-/** Called when the frame rate spin widget has been changed */
-void
-FilmEditor::frame_rate_spin_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_video_frame_rate (_frame_rate_spin->GetValue ());
-}
-
-void
-FilmEditor::audio_channels_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_audio_channels (_audio_channels->GetValue ());
-}
-
-void
-FilmEditor::resolution_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_resolution (_resolution->GetSelection() == 0 ? RESOLUTION_2K : RESOLUTION_4K);
-}
-
-void
-FilmEditor::standard_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_interop (_standard->GetSelection() == 1);
-}
/** Called when the metadata stored in the Film object has changed;
* so that we can update the GUI.
return;
}
- SafeStringStream s;
-
- for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
- (*i)->film_changed (p);
- }
-
- switch (p) {
- case Film::NONE:
- break;
- case Film::CONTENT:
- setup_content ();
- break;
- case Film::CONTAINER:
- setup_container ();
- break;
- case Film::NAME:
- checked_set (_name, _film->name());
- setup_dcp_name ();
- break;
- case Film::WITH_SUBTITLES:
- setup_dcp_name ();
- break;
- case Film::DCP_CONTENT_TYPE:
- checked_set (_dcp_content_type, DCPContentType::as_index (_film->dcp_content_type ()));
- setup_dcp_name ();
- break;
- case Film::SCALER:
- checked_set (_scaler, Scaler::as_index (_film->scaler ()));
- break;
- case Film::SIGNED:
- checked_set (_signed, _film->is_signed ());
- break;
- case Film::ENCRYPTED:
- checked_set (_encrypted, _film->encrypted ());
- if (_film->encrypted ()) {
- _film->set_signed (true);
- _signed->Enable (false);
- } else {
- _signed->Enable (_generally_sensitive);
- }
- break;
- case Film::RESOLUTION:
- checked_set (_resolution, _film->resolution() == RESOLUTION_2K ? 0 : 1);
- setup_dcp_name ();
- break;
- case Film::J2K_BANDWIDTH:
- checked_set (_j2k_bandwidth, _film->j2k_bandwidth() / 1000000);
- break;
- case Film::USE_ISDCF_NAME:
- checked_set (_use_isdcf_name, _film->use_isdcf_name ());
- setup_dcp_name ();
- break;
- case Film::ISDCF_METADATA:
- setup_dcp_name ();
- break;
- case Film::VIDEO_FRAME_RATE:
- {
- bool done = false;
- for (unsigned int i = 0; i < _frame_rate_choice->GetCount(); ++i) {
- if (wx_to_std (_frame_rate_choice->GetString(i)) == boost::lexical_cast<string> (_film->video_frame_rate())) {
- checked_set (_frame_rate_choice, i);
- done = true;
- break;
- }
- }
-
- if (!done) {
- checked_set (_frame_rate_choice, -1);
- }
-
- _frame_rate_spin->SetValue (_film->video_frame_rate ());
-
- _best_frame_rate->Enable (_film->best_video_frame_rate () != _film->video_frame_rate ());
- break;
- }
- case Film::AUDIO_CHANNELS:
- checked_set (_audio_channels, _film->audio_channels ());
- setup_dcp_name ();
- break;
- case Film::SEQUENCE_VIDEO:
- checked_set (_sequence_video, _film->sequence_video ());
- break;
- case Film::THREE_D:
- checked_set (_three_d, _film->three_d ());
- setup_dcp_name ();
- break;
- case Film::INTEROP:
- checked_set (_standard, _film->interop() ? 1 : 0);
- break;
- }
+ _content_panel->film_changed (p);
+ _dcp_panel->film_changed (p);
-
}
void
return;
}
- for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
- (*i)->film_content_changed (property);
- }
-
- if (property == FFmpegContentProperty::AUDIO_STREAM) {
- setup_dcp_name ();
- } else if (property == ContentProperty::PATH) {
- setup_content ();
- } else if (property == ContentProperty::POSITION) {
- setup_content ();
- }
-}
-
-void
-FilmEditor::setup_container ()
-{
- int n = 0;
- vector<Ratio const *> ratios = Ratio::all ();
- vector<Ratio const *>::iterator i = ratios.begin ();
- while (i != ratios.end() && *i != _film->container ()) {
- ++i;
- ++n;
- }
-
- if (i == ratios.end()) {
- checked_set (_container, -1);
- } else {
- checked_set (_container, n);
- }
-
- setup_dcp_name ();
-}
-
-/** Called when the container widget has been changed */
-void
-FilmEditor::container_changed ()
-{
- if (!_film) {
- return;
- }
-
- int const n = _container->GetSelection ();
- if (n >= 0) {
- vector<Ratio const *> ratios = Ratio::all ();
- assert (n < int (ratios.size()));
- _film->set_container (ratios[n]);
- }
-}
-
-/** Called when the DCP content type widget has been changed */
-void
-FilmEditor::dcp_content_type_changed ()
-{
- if (!_film) {
- return;
- }
-
- int const n = _dcp_content_type->GetSelection ();
- if (n != wxNOT_FOUND) {
- _film->set_dcp_content_type (DCPContentType::from_index (n));
- }
+ _content_panel->film_content_changed (property);
+ _dcp_panel->film_content_changed (property);
}
/** Sets the Film that we are editing */
_film = f;
+ _content_panel->set_film (_film);
+ _dcp_panel->set_film (_film);
+
if (_film) {
_film->Changed.connect (bind (&FilmEditor::film_changed, this, _1));
_film->ContentChanged.connect (bind (&FilmEditor::film_content_changed, this, _2));
FileChanged ("");
}
- film_changed (Film::NAME);
- film_changed (Film::USE_ISDCF_NAME);
- film_changed (Film::CONTENT);
- film_changed (Film::DCP_CONTENT_TYPE);
- film_changed (Film::CONTAINER);
- film_changed (Film::RESOLUTION);
- film_changed (Film::SCALER);
- film_changed (Film::WITH_SUBTITLES);
- film_changed (Film::SIGNED);
- film_changed (Film::ENCRYPTED);
- film_changed (Film::J2K_BANDWIDTH);
- film_changed (Film::ISDCF_METADATA);
- film_changed (Film::VIDEO_FRAME_RATE);
- film_changed (Film::AUDIO_CHANNELS);
- film_changed (Film::SEQUENCE_VIDEO);
- film_changed (Film::THREE_D);
- film_changed (Film::INTEROP);
-
if (!_film->content().empty ()) {
- set_selection (_film->content().front ());
+ _content_panel->set_selection (_film->content().front ());
}
-
- content_selection_changed ();
}
void
FilmEditor::set_general_sensitivity (bool s)
{
- _generally_sensitive = s;
-
- /* Stuff in the Content / DCP tabs */
- _name->Enable (s);
- _use_isdcf_name->Enable (s);
- _edit_isdcf_button->Enable (s);
- _content->Enable (s);
- _content_add_file->Enable (s);
- _content_add_folder->Enable (s);
- _content_remove->Enable (s);
- _content_earlier->Enable (s);
- _content_later->Enable (s);
- _content_timeline->Enable (s);
- _dcp_content_type->Enable (s);
-
- bool si = s;
- if (_film && _film->encrypted ()) {
- si = false;
- }
- _signed->Enable (si);
-
- _encrypted->Enable (s);
- _frame_rate_choice->Enable (s);
- _frame_rate_spin->Enable (s);
- _audio_channels->Enable (s);
- _j2k_bandwidth->Enable (s);
- _container->Enable (s);
- _best_frame_rate->Enable (s && _film && _film->best_video_frame_rate () != _film->video_frame_rate ());
- _sequence_video->Enable (s);
- _resolution->Enable (s);
- _scaler->Enable (s);
- _three_d->Enable (s);
- _standard->Enable (s);
-
- /* Set the panels in the content notebook */
- for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
- (*i)->Enable (s);
- }
-}
-
-/** Called when the scaler widget has been changed */
-void
-FilmEditor::scaler_changed ()
-{
- if (!_film) {
- return;
- }
-
- int const n = _scaler->GetSelection ();
- if (n >= 0) {
- _film->set_scaler (Scaler::from_index (n));
- }
-}
-
-void
-FilmEditor::use_isdcf_name_toggled ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_use_isdcf_name (_use_isdcf_name->GetValue ());
-}
-
-void
-FilmEditor::edit_isdcf_button_clicked ()
-{
- if (!_film) {
- return;
- }
-
- ISDCFMetadataDialog* d = new ISDCFMetadataDialog (this, _film->isdcf_metadata ());
- d->ShowModal ();
- _film->set_isdcf_metadata (d->isdcf_metadata ());
- d->Destroy ();
+ _content_panel->set_general_sensitivity (s);
+ _dcp_panel->set_general_sensitivity (s);
}
void
{
set_general_sensitivity (!a);
}
-
-void
-FilmEditor::setup_dcp_name ()
-{
- string s = _film->dcp_name (true);
- if (s.length() > 28) {
- _dcp_name->SetLabel (std_to_wx (s.substr (0, 28)) + N_("..."));
- _dcp_name->SetToolTip (std_to_wx (s));
- } else {
- _dcp_name->SetLabel (std_to_wx (s));
- }
-}
-
-void
-FilmEditor::best_frame_rate_clicked ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_video_frame_rate (_film->best_video_frame_rate ());
-}
-
-void
-FilmEditor::setup_content ()
-{
- string selected_summary;
- int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
- if (s != -1) {
- selected_summary = wx_to_std (_content->GetItemText (s));
- }
-
- _content->DeleteAllItems ();
-
- ContentList content = _film->content ();
- sort (content.begin(), content.end(), ContentSorter ());
-
- for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
- int const t = _content->GetItemCount ();
- bool const valid = (*i)->paths_valid ();
-
- string s = (*i)->summary ();
- if (!valid) {
- s = _("MISSING: ") + s;
- }
-
- _content->InsertItem (t, std_to_wx (s));
-
- if ((*i)->summary() == selected_summary) {
- _content->SetItemState (t, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
- }
-
- if (!valid) {
- _content->SetItemTextColour (t, *wxRED);
- }
- }
-
- if (selected_summary.empty () && !content.empty ()) {
- /* Select the item of content if none was selected before */
- _content->SetItemState (0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
- }
-}
-
-void
-FilmEditor::content_add_file_clicked ()
-{
- /* The wxFD_CHANGE_DIR here prevents a `could not set working directory' error 123 on Windows when using
- non-Latin filenames or paths.
- */
- wxFileDialog* d = new wxFileDialog (this, _("Choose a file or files"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE | wxFD_CHANGE_DIR);
- int const r = d->ShowModal ();
-
- if (r != wxID_OK) {
- d->Destroy ();
- return;
- }
-
- wxArrayString paths;
- d->GetPaths (paths);
-
- /* XXX: check for lots of files here and do something */
-
- for (unsigned int i = 0; i < paths.GetCount(); ++i) {
- _film->examine_and_add_content (content_factory (_film, wx_to_std (paths[i])));
- }
-
- d->Destroy ();
-}
-
-void
-FilmEditor::content_add_folder_clicked ()
-{
- wxDirDialog* d = new wxDirDialog (this, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST);
- int const r = d->ShowModal ();
- d->Destroy ();
-
- if (r != wxID_OK) {
- return;
- }
-
- shared_ptr<ImageContent> ic;
-
- try {
- ic.reset (new ImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
- } catch (FileError& e) {
- error_dialog (this, std_to_wx (e.what ()));
- return;
- }
-
- _film->examine_and_add_content (ic);
-}
-
-void
-FilmEditor::content_remove_clicked ()
-{
- ContentList c = selected_content ();
- for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- _film->remove_content (*i);
- }
-
- content_selection_changed ();
-}
-
-void
-FilmEditor::content_selection_changed ()
-{
- setup_content_sensitivity ();
-
- for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
- (*i)->content_selection_changed ();
- }
-}
-
-/** Set up broad sensitivity based on the type of content that is selected */
-void
-FilmEditor::setup_content_sensitivity ()
-{
- _content_add_file->Enable (_generally_sensitive);
- _content_add_folder->Enable (_generally_sensitive);
-
- ContentList selection = selected_content ();
- VideoContentList video_selection = selected_video_content ();
- AudioContentList audio_selection = selected_audio_content ();
-
- _content_remove->Enable (!selection.empty() && _generally_sensitive);
- _content_earlier->Enable (selection.size() == 1 && _generally_sensitive);
- _content_later->Enable (selection.size() == 1 && _generally_sensitive);
- _content_timeline->Enable (!_film->content().empty() && _generally_sensitive);
-
- _video_panel->Enable (!video_selection.empty() && _generally_sensitive);
- _audio_panel->Enable (!audio_selection.empty() && _generally_sensitive);
- _subtitle_panel->Enable (selection.size() == 1 && dynamic_pointer_cast<FFmpegContent> (selection.front()) && _generally_sensitive);
- _timing_panel->Enable (!selection.empty() && _generally_sensitive);
-}
-
-ContentList
-FilmEditor::selected_content ()
-{
- ContentList sel;
-
- if (!_film) {
- return sel;
- }
-
- /* The list was populated using a sorted content list, so we must sort it here too
- so that we can look up by index and get the right thing.
- */
- ContentList content = _film->content ();
- sort (content.begin(), content.end(), ContentSorter ());
-
- long int s = -1;
- while (true) {
- s = _content->GetNextItem (s, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
- if (s == -1) {
- break;
- }
-
- if (s < int (_film->content().size ())) {
- sel.push_back (content[s]);
- }
- }
-
- return sel;
-}
-
-VideoContentList
-FilmEditor::selected_video_content ()
-{
- ContentList c = selected_content ();
- VideoContentList vc;
-
- for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- shared_ptr<VideoContent> t = dynamic_pointer_cast<VideoContent> (*i);
- if (t) {
- vc.push_back (t);
- }
- }
-
- return vc;
-}
-
-AudioContentList
-FilmEditor::selected_audio_content ()
-{
- ContentList c = selected_content ();
- AudioContentList ac;
-
- for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- shared_ptr<AudioContent> t = dynamic_pointer_cast<AudioContent> (*i);
- if (t) {
- ac.push_back (t);
- }
- }
-
- return ac;
-}
-
-SubtitleContentList
-FilmEditor::selected_subtitle_content ()
-{
- ContentList c = selected_content ();
- SubtitleContentList sc;
-
- for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- shared_ptr<SubtitleContent> t = dynamic_pointer_cast<SubtitleContent> (*i);
- if (t) {
- sc.push_back (t);
- }
- }
-
- return sc;
-}
-
-FFmpegContentList
-FilmEditor::selected_ffmpeg_content ()
-{
- ContentList c = selected_content ();
- FFmpegContentList sc;
-
- for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- shared_ptr<FFmpegContent> t = dynamic_pointer_cast<FFmpegContent> (*i);
- if (t) {
- sc.push_back (t);
- }
- }
-
- return sc;
-}
-
-void
-FilmEditor::content_timeline_clicked ()
-{
- if (_timeline_dialog) {
- _timeline_dialog->Destroy ();
- _timeline_dialog = 0;
- }
-
- _timeline_dialog = new TimelineDialog (this, _film);
- _timeline_dialog->Show ();
-}
-
-void
-FilmEditor::set_selection (weak_ptr<Content> wc)
-{
- ContentList content = _film->content ();
- for (size_t i = 0; i < content.size(); ++i) {
- if (content[i] == wc.lock ()) {
- _content->SetItemState (i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
- } else {
- _content->SetItemState (i, 0, wxLIST_STATE_SELECTED);
- }
- }
-}
-
-void
-FilmEditor::sequence_video_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_sequence_video (_sequence_video->GetValue ());
-}
-
-void
-FilmEditor::content_right_click (wxListEvent& ev)
-{
- _menu.popup (_film, selected_content (), ev.GetPoint ());
-}
-
-void
-FilmEditor::three_d_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_three_d (_three_d->GetValue ());
-}
-
-void
-FilmEditor::content_earlier_clicked ()
-{
- ContentList sel = selected_content ();
- if (sel.size() == 1) {
- _film->move_content_earlier (sel.front ());
- content_selection_changed ();
- }
-}
-
-void
-FilmEditor::content_later_clicked ()
-{
- ContentList sel = selected_content ();
- if (sel.size() == 1) {
- _film->move_content_later (sel.front ());
- content_selection_changed ();
- }
-}
-
-void
-FilmEditor::config_changed ()
-{
- _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
- setup_frame_rate_widget ();
-}
-
-void
-FilmEditor::setup_frame_rate_widget ()
-{
- if (Config::instance()->allow_any_dcp_frame_rate ()) {
- _frame_rate_choice->Hide ();
- _frame_rate_spin->Show ();
- } else {
- _frame_rate_choice->Show ();
- _frame_rate_spin->Hide ();
- }
--
- _frame_rate_sizer->Layout ();
-}
#include <boost/bind.hpp>
#include "lib/film.h"
#include "lib/config.h"
+ #include "lib/safe_stringstream.h"
#include "properties_dialog.h"
#include "wx_util.h"
using std::string;
- using std::stringstream;
using std::fixed;
using std::setprecision;
using boost::shared_ptr;
add (_("Frames already encoded"), true);
_encoded = add (new ThreadedStaticText (this, _("counting..."), boost::bind (&PropertiesDialog::frames_already_encoded, this)));
_encoded->Finished.connect (boost::bind (&PropertiesDialog::layout, this));
-
- _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->time_to_video_frames (_film->length()))));
+ _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->length().frames (_film->video_frame_rate ()))));
double const disk = double (_film->required_disk_space()) / 1073741824.0f;
- stringstream s;
+ SafeStringStream s;
s << fixed << setprecision (1) << disk << wx_to_std (_("Gb"));
_disk->SetLabel (std_to_wx (s.str ()));
string
PropertiesDialog::frames_already_encoded () const
{
- stringstream u;
+ SafeStringStream u;
try {
u << _film->encoded_frames ();
} catch (boost::thread_interrupted &) {
return "";
}
-
- if (_film->length()) {
+
+ uint64_t const frames = _film->length().frames (_film->video_frame_rate ());
+ if (frames) {
/* XXX: encoded_frames() should check which frames have been encoded */
- u << " (" << (_film->encoded_frames() * 100 / _film->time_to_video_frames (_film->length())) << "%)";
+ u << " (" << (_film->encoded_frames() * 100 / frames) << "%)";
}
return u.str ();
}
*/
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "lib/content.h"
#include "lib/image_content.h"
#include "timing_panel.h"
#include "wx_util.h"
#include "timecode.h"
-#include "film_editor.h"
+#include "content_panel.h"
using std::cout;
using std::string;
using std::set;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
-using libdcp::raw_convert;
+using dcp::raw_convert;
-TimingPanel::TimingPanel (FilmEditor* e)
+TimingPanel::TimingPanel (ContentPanel* p)
/* horrid hack for apparent lack of context support with wxWidgets i18n code */
- : FilmEditorPanel (e, S_("Timing|Timing"))
+ : ContentSubPanel (p, S_("Timing|Timing"))
{
wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
_sizer->Add (grid, 0, wxALL, 8);
void
TimingPanel::film_content_changed (int property)
{
- ContentList cl = _editor->selected_content ();
- int const film_video_frame_rate = _editor->film()->video_frame_rate ();
+ ContentList cl = _parent->selected ();
+ int const film_video_frame_rate = _parent->film()->video_frame_rate ();
/* Here we check to see if we have exactly one different value of various
properties, and fill the controls with that value if so.
if (property == ContentProperty::POSITION) {
- set<Time> check;
+ set<DCPTime> check;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
check.insert ((*i)->position ());
}
property == VideoContentProperty::VIDEO_FRAME_TYPE
) {
- set<Time> check;
+ set<DCPTime> check;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
check.insert ((*i)->full_length ());
}
} else if (property == ContentProperty::TRIM_START) {
- set<Time> check;
+ set<DCPTime> check;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
check.insert ((*i)->trim_start ());
}
} else if (property == ContentProperty::TRIM_END) {
- set<Time> check;
+ set<DCPTime> check;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
check.insert ((*i)->trim_end ());
}
if (check.size() == 1) {
_trim_end->set (cl.front()->trim_end (), film_video_frame_rate);
} else {
- _trim_end->set (0, 24);
+ _trim_end->clear ();
}
}
property == VideoContentProperty::VIDEO_FRAME_TYPE
) {
- set<Time> check;
+ set<DCPTime> check;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
check.insert ((*i)->length_after_trim ());
}
set<float> check;
shared_ptr<VideoContent> vc;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
- vc = dynamic_pointer_cast<VideoContent> (*i);
- if (vc) {
- check.insert (vc->video_frame_rate ());
+ shared_ptr<VideoContent> t = dynamic_pointer_cast<VideoContent> (*i);
+ if (t) {
+ check.insert (t->video_frame_rate ());
+ vc = t;
}
}
if (check.size() == 1) {
void
TimingPanel::position_changed ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- (*i)->set_position (_position->get (_editor->film()->video_frame_rate ()));
+ (*i)->set_position (_position->get (_parent->film()->video_frame_rate ()));
}
}
void
TimingPanel::full_length_changed ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (*i);
if (ic && ic->still ()) {
- ic->set_video_length (rint (_full_length->get (_editor->film()->video_frame_rate()) * ic->video_frame_rate() / TIME_HZ));
+ /* XXX: No effective FRC here... is this right? */
+ ic->set_video_length (ContentTime (_full_length->get (_parent->film()->video_frame_rate()), FrameRateChange (1, 1)));
}
}
}
void
TimingPanel::trim_start_changed ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- (*i)->set_trim_start (_trim_start->get (_editor->film()->video_frame_rate ()));
+ (*i)->set_trim_start (_trim_start->get (_parent->film()->video_frame_rate ()));
}
}
void
TimingPanel::trim_end_changed ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- (*i)->set_trim_end (_trim_end->get (_editor->film()->video_frame_rate ()));
+ (*i)->set_trim_end (_trim_end->get (_parent->film()->video_frame_rate ()));
}
}
void
TimingPanel::play_length_changed ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- (*i)->set_trim_end ((*i)->full_length() - _play_length->get (_editor->film()->video_frame_rate()) - (*i)->trim_start());
+ (*i)->set_trim_end ((*i)->full_length() - _play_length->get (_parent->film()->video_frame_rate()) - (*i)->trim_start());
}
}
void
TimingPanel::set_video_frame_rate ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
if (vc) {
void
TimingPanel::content_selection_changed ()
{
- bool const e = !_editor->selected_content().empty ();
+ bool const e = !_parent->selected().empty ();
_position->Enable (e);
_full_length->Enable (e);
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ 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
*/
- #include <sstream>
+/** @file test/film_metadata_test.cc
+ * @brief Test some basic reading/writing of film metadata.
+ */
+
#include <boost/test/unit_test.hpp>
#include <boost/filesystem.hpp>
#include <boost/date_time.hpp>
BOOST_AUTO_TEST_CASE (film_metadata_test)
{
- string const test_film = "build/test/film_metadata_test";
-
- if (boost::filesystem::exists (test_film)) {
- boost::filesystem::remove_all (test_film);
- }
+ shared_ptr<Film> f = new_test_film ("film_metadata_test");
+ boost::filesystem::path dir = test_film_dir ("film_metadata_test");
- shared_ptr<Film> f (new Film (test_film));
f->_isdcf_date = boost::gregorian::from_undelimited_string ("20130211");
BOOST_CHECK (f->container() == 0);
BOOST_CHECK (f->dcp_content_type() == 0);
list<string> ignore;
ignore.push_back ("Key");
- check_xml ("test/data/metadata.xml.ref", test_film + "/metadata.xml", ignore);
+ check_xml ("test/data/metadata.xml.ref", dir.string() + "/metadata.xml", ignore);
- shared_ptr<Film> g (new Film (test_film));
+ shared_ptr<Film> g (new Film (dir));
g->read_metadata ();
BOOST_CHECK_EQUAL (g->name(), "fred");
BOOST_CHECK_EQUAL (g->container(), Ratio::from_id ("185"));
g->write_metadata ();
- check_xml ("test/data/metadata.xml.ref", test_film + "/metadata.xml", ignore);
+ check_xml ("test/data/metadata.xml.ref", dir.string() + "/metadata.xml", ignore);
}