#include <fstream>
#include <glib.h>
#include <boost/filesystem.hpp>
+ #include <libdcp/colour_matrix.h>
+ #include <libcxml/cxml.h>
#include "config.h"
#include "server.h"
#include "scaler.h"
#include "filter.h"
+ #include "ratio.h"
+ #include "dcp_content_type.h"
#include "sound_processor.h"
- #include "cinema.h"
+ #include "colour_conversion.h"
+
+ #include "i18n.h"
using std::vector;
using std::ifstream;
using std::string;
using std::ofstream;
using std::list;
+ using std::max;
using boost::shared_ptr;
+ using boost::lexical_cast;
+ using boost::optional;
Config* Config::_instance = 0;
/** Construct default configuration */
Config::Config ()
- : _num_local_encoding_threads (2)
+ : _num_local_encoding_threads (max (2U, boost::thread::hardware_concurrency()))
, _server_port (6192)
- , _reference_scaler (Scaler::from_id ("bicubic"))
- , _tms_path (".")
- , _sound_processor (SoundProcessor::from_id ("dolby_cp750"))
+ , _tms_path (N_("."))
+ , _sound_processor (SoundProcessor::from_id (N_("dolby_cp750")))
+ , _default_still_length (10)
+ , _default_container (Ratio::from_id ("185"))
+ , _default_dcp_content_type (DCPContentType::from_dci_name ("TST"))
+ , _default_j2k_bandwidth (200000000)
{
- ifstream f (read_file().c_str ());
- string line;
+ _allowed_dcp_frame_rates.push_back (24);
+ _allowed_dcp_frame_rates.push_back (25);
+ _allowed_dcp_frame_rates.push_back (30);
+ _allowed_dcp_frame_rates.push_back (48);
+ _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));
+ }
+
+ void
+ Config::read ()
+ {
+ if (!boost::filesystem::exists (file (false))) {
+ read_old_metadata ();
+ return;
+ }
- shared_ptr<Cinema> cinema;
- shared_ptr<Screen> screen;
+ cxml::Document f ("Config");
+ f.read_file (file (false));
+ optional<string> c;
+
+ _num_local_encoding_threads = f.number_child<int> ("NumLocalEncodingThreads");
+ _default_directory = f.string_child ("DefaultDirectory");
+ _server_port = f.number_child<int> ("ServerPort");
+ list<shared_ptr<cxml::Node> > servers = f.node_children ("Server");
+ for (list<shared_ptr<cxml::Node> >::iterator i = servers.begin(); i != servers.end(); ++i) {
+ _servers.push_back (ServerDescription (*i));
+ }
+
+ _tms_ip = f.string_child ("TMSIP");
+ _tms_path = f.string_child ("TMSPath");
+ _tms_user = f.string_child ("TMSUser");
+ _tms_password = f.string_child ("TMSPassword");
+
+ c = f.optional_string_child ("SoundProcessor");
+ if (c) {
+ _sound_processor = SoundProcessor::from_id (c.get ());
+ }
+
+ _language = f.optional_string_child ("Language");
+
+ c = f.optional_string_child ("DefaultContainer");
+ if (c) {
+ _default_container = Ratio::from_id (c.get ());
+ }
+
+ c = f.optional_string_child ("DefaultDCPContentType");
+ if (c) {
+ _default_dcp_content_type = DCPContentType::from_dci_name (c.get ());
+ }
+
+ _dcp_metadata.issuer = f.optional_string_child ("DCPMetadataIssuer").get_value_or ("");
+ _dcp_metadata.creator = f.optional_string_child ("DCPMetadataCreator").get_value_or ("");
+
+ _default_dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
+ _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
+ _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
+
+ list<shared_ptr<cxml::Node> > cc = f.node_children ("ColourConversion");
+
+ if (!cc.empty ()) {
+ _colour_conversions.clear ();
+ }
+
+ for (list<shared_ptr<cxml::Node> >::iterator i = cc.begin(); i != cc.end(); ++i) {
+ _colour_conversions.push_back (PresetColourConversion (*i));
+ }
+ }
+
+ void
+ Config::read_old_metadata ()
+ {
+ ifstream f (file(true).c_str ());
+ string line;
++
while (getline (f, line)) {
if (line.empty ()) {
continue;
string const k = line.substr (0, s);
string const v = line.substr (s + 1);
- if (k == "num_local_encoding_threads") {
+ if (k == N_("num_local_encoding_threads")) {
_num_local_encoding_threads = atoi (v.c_str ());
- } else if (k == "default_directory") {
+ } else if (k == N_("default_directory")) {
_default_directory = v;
- } else if (k == "server_port") {
+ } else if (k == N_("server_port")) {
_server_port = atoi (v.c_str ());
- } else if (k == "reference_scaler") {
- _reference_scaler = Scaler::from_id (v);
- } else if (k == "reference_filter") {
- _reference_filters.push_back (Filter::from_id (v));
- } else if (k == "server") {
- _servers.push_back (ServerDescription::create_from_metadata (v));
- } else if (k == "tms_ip") {
+ } else if (k == N_("server")) {
+ optional<ServerDescription> server = ServerDescription::create_from_metadata (v);
+ if (server) {
+ _servers.push_back (server.get ());
+ }
+ } else if (k == N_("tms_ip")) {
_tms_ip = v;
- } else if (k == "tms_path") {
+ } else if (k == N_("tms_path")) {
_tms_path = v;
- } else if (k == "tms_user") {
+ } else if (k == N_("tms_user")) {
_tms_user = v;
- } else if (k == "tms_password") {
+ } else if (k == N_("tms_password")) {
_tms_password = v;
- } else if (k == "sound_processor") {
+ } else if (k == N_("sound_processor")) {
_sound_processor = SoundProcessor::from_id (v);
- } else if (k == "cinema") {
- if (cinema) {
- _cinemas.push_back (cinema);
- }
- cinema.reset (new Cinema (v, ""));
- } else if (k == "cinema_email") {
- assert (cinema);
- cinema->email = v;
- } else if (k == "screen") {
- assert (cinema);
- if (screen) {
- cinema->screens.push_back (screen);
- }
- screen.reset (new Screen (v, shared_ptr<libdcp::Certificate> ()));
- } else if (k == "screen_certificate") {
- assert (screen);
- shared_ptr<Certificate> c (new libdcp::Certificate);
- c->set_from_string (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_dci_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 (cinema) {
- _cinemas.push_back (cinema);
+ _default_dci_metadata.read_old_metadata (k, v);
}
}
/** @return Filename to write configuration to */
string
- Config::write_file () const
+ Config::file (bool old) const
{
boost::filesystem::path p;
p /= g_get_user_config_dir ();
- p /= "dvdomatic";
- boost::filesystem::create_directory (p);
- p /= "config";
- return p.string ();
- }
-
- string
- Config::read_file () const
- {
- if (boost::filesystem::exists (write_file ())) {
- return write_file ();
+ boost::system::error_code ec;
+ boost::filesystem::create_directory (p, ec);
+ if (old) {
+ p /= ".dvdomatic";
+ } else {
+ p /= "dcpomatic.xml";
}
-
- boost::filesystem::path p;
- p /= g_get_user_config_dir ();
- p /= ".dvdomatic";
return p.string ();
}
+string
+Config::crypt_chain_directory () const
+{
+ boost::filesystem::path p;
+ p /= g_get_user_config_dir ();
+ p /= "dvdomatic";
+ p /= "crypt";
+ boost::filesystem::create_directories (p);
+ return p.string ();
+}
+
/** @return Singleton instance */
Config *
Config::instance ()
{
if (_instance == 0) {
_instance = new Config;
+ try {
+ _instance->read ();
+ } catch (...) {
+ /* configuration load failed; never mind, just
+ stick with the default.
+ */
+ }
}
return _instance;
void
Config::write () const
{
- ofstream f (write_file().c_str ());
- f << "num_local_encoding_threads " << _num_local_encoding_threads << "\n"
- << "default_directory " << _default_directory << "\n"
- << "server_port " << _server_port << "\n"
- << "reference_scaler " << _reference_scaler->id () << "\n";
+ xmlpp::Document doc;
+ xmlpp::Element* root = doc.create_root_node ("Config");
- for (vector<Filter const *>::const_iterator i = _reference_filters.begin(); i != _reference_filters.end(); ++i) {
- f << "reference_filter " << (*i)->id () << "\n";
- }
+ root->add_child("NumLocalEncodingThreads")->add_child_text (lexical_cast<string> (_num_local_encoding_threads));
+ root->add_child("DefaultDirectory")->add_child_text (_default_directory);
+ root->add_child("ServerPort")->add_child_text (lexical_cast<string> (_server_port));
- for (vector<ServerDescription*>::const_iterator i = _servers.begin(); i != _servers.end(); ++i) {
- f << "server " << (*i)->as_metadata () << "\n";
+ for (vector<ServerDescription>::const_iterator i = _servers.begin(); i != _servers.end(); ++i) {
+ i->as_xml (root->add_child ("Server"));
}
- f << "tms_ip " << _tms_ip << "\n";
- f << "tms_path " << _tms_path << "\n";
- f << "tms_user " << _tms_user << "\n";
- f << "tms_password " << _tms_password << "\n";
- f << "sound_processor " << _sound_processor->id () << "\n";
+ root->add_child("TMSIP")->add_child_text (_tms_ip);
+ 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 (_language) {
+ root->add_child("Language")->add_child_text (_language.get());
+ }
+ if (_default_container) {
+ root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
+ }
+ if (_default_dcp_content_type) {
+ root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->dci_name ());
+ }
+ root->add_child("DCPMetadataIssuer")->add_child_text (_dcp_metadata.issuer);
+ root->add_child("DCPMetadataCreator")->add_child_text (_dcp_metadata.creator);
- for (list<shared_ptr<Cinema> >::const_iterator i = _cinemas.begin(); i != _cinemas.end(); ++i) {
- f << "cinema " << (*i)->name << "\n";
- f << "cinema_email " << (*i)->email << "\n";
- for (list<shared_ptr<Screen> >::iterator j = (*i)->screens.begin(); j != (*i)->screens.end(); ++j) {
- f << "screen " << (*j)->name << "\n";
- }
+ _default_dci_metadata.as_xml (root->add_child ("DCIMetadata"));
+
+ root->add_child("DefaultStillLength")->add_child_text (lexical_cast<string> (_default_still_length));
+ root->add_child("DefaultJ2KBandwidth")->add_child_text (lexical_cast<string> (_default_j2k_bandwidth));
+
+ for (vector<PresetColourConversion>::const_iterator i = _colour_conversions.begin(); i != _colour_conversions.end(); ++i) {
+ i->as_xml (root->add_child ("ColourConversion"));
}
+
+ doc.write_to_file_formatted (file (false));
}
string
return _default_directory;
}
+
+ void
+ Config::drop ()
+ {
+ delete _instance;
+ _instance = 0;
+ }
* @brief Class holding configuration.
*/
- #ifndef DVDOMATIC_CONFIG_H
- #define DVDOMATIC_CONFIG_H
+ #ifndef DCPOMATIC_CONFIG_H
+ #define DCPOMATIC_CONFIG_H
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
+ #include <libdcp/metadata.h>
+ #include "dci_metadata.h"
+ #include "colour_conversion.h"
+ #include "server.h"
class ServerDescription;
class Scaler;
class Filter;
class SoundProcessor;
+ class DCPContentType;
+ class Ratio;
+class Cinema;
/** @class Config
* @brief A singleton class holding configuration.
*/
- class Config
+ class Config : public boost::noncopyable
{
public:
}
/** @return J2K encoding servers to use */
- std::vector<ServerDescription*> servers () const {
+ std::vector<ServerDescription> servers () const {
return _servers;
}
- Scaler const * reference_scaler () const {
- return _reference_scaler;
- }
-
- std::vector<Filter const *> reference_filters () const {
- return _reference_filters;
- }
-
/** @return The IP address of a TMS that we can copy DCPs to */
std::string tms_ip () const {
return _tms_ip;
return _sound_processor;
}
+ std::list<boost::shared_ptr<Cinema> > cinemas () const {
+ return _cinemas;
+ }
++
+ std::list<int> allowed_dcp_frame_rates () const {
+ return _allowed_dcp_frame_rates;
+ }
+
+ DCIMetadata default_dci_metadata () const {
+ return _default_dci_metadata;
+ }
+
+ boost::optional<std::string> language () const {
+ return _language;
+ }
+
+ int default_still_length () const {
+ return _default_still_length;
+ }
+
+ Ratio const * default_container () const {
+ return _default_container;
+ }
+
+ DCPContentType const * default_dcp_content_type () const {
+ return _default_dcp_content_type;
+ }
+
+ libdcp::XMLMetadata dcp_metadata () const {
+ return _dcp_metadata;
+ }
+
+ int default_j2k_bandwidth () const {
+ return _default_j2k_bandwidth;
+ }
+
+ std::vector<PresetColourConversion> colour_conversions () const {
+ return _colour_conversions;
+ }
/** @param n New number of local encoding threads */
void set_num_local_encoding_threads (int n) {
}
/** @param s New list of servers */
- void set_servers (std::vector<ServerDescription*> s) {
+ void set_servers (std::vector<ServerDescription> s) {
_servers = s;
}
_tms_password = p;
}
+ void add_cinema (boost::shared_ptr<Cinema> c) {
+ _cinemas.push_back (c);
+ }
+
+ void remove_cinema (boost::shared_ptr<Cinema> c) {
+ _cinemas.remove (c);
+ }
++
+ void set_allowed_dcp_frame_rates (std::list<int> const & r) {
+ _allowed_dcp_frame_rates = r;
+ }
+
+ void set_default_dci_metadata (DCIMetadata d) {
+ _default_dci_metadata = d;
+ }
+
+ void set_language (std::string l) {
+ _language = l;
+ }
+
+ void unset_language () {
+ _language = boost::none;
+ }
+
+ void set_default_still_length (int s) {
+ _default_still_length = s;
+ }
+
+ void set_default_container (Ratio const * c) {
+ _default_container = c;
+ }
+
+ void set_default_dcp_content_type (DCPContentType const * t) {
+ _default_dcp_content_type = t;
+ }
+
+ void set_dcp_metadata (libdcp::XMLMetadata m) {
+ _dcp_metadata = m;
+ }
+
+ void set_default_j2k_bandwidth (int b) {
+ _default_j2k_bandwidth = b;
+ }
+
+ void set_colour_conversions (std::vector<PresetColourConversion> const & c) {
+ _colour_conversions = c;
+ }
void write () const;
+ std::string crypt_chain_directory () const;
+
static Config* instance ();
+ static void drop ();
private:
Config ();
- std::string read_file () const;
- std::string write_file () const;
+ std::string file (bool) const;
+ void read ();
+ void read_old_metadata ();
/** number of threads to use for J2K encoding on the local machine */
int _num_local_encoding_threads;
int _server_port;
/** J2K encoding servers to use */
- std::vector<ServerDescription *> _servers;
+ std::vector<ServerDescription> _servers;
/** Scaler to use for the "A" part of A/B comparisons */
Scaler const * _reference_scaler;
/** Filters to use for the "A" part of A/B comparisons */
std::string _tms_password;
/** Our sound processor */
SoundProcessor const * _sound_processor;
+ std::list<int> _allowed_dcp_frame_rates;
+ /** Default DCI metadata for newly-created Films */
+ DCIMetadata _default_dci_metadata;
+ boost::optional<std::string> _language;
+ int _default_still_length;
+ Ratio const * _default_container;
+ DCPContentType const * _default_dcp_content_type;
+ libdcp::XMLMetadata _dcp_metadata;
+ int _default_j2k_bandwidth;
+ std::vector<PresetColourConversion> _colour_conversions;
+ std::list<boost::shared_ptr<Cinema> > _cinemas;
+
/** Singleton instance, or 0 */
static Config* _instance;
};
*/
+ #ifndef DCPOMATIC_EXCEPTIONS_H
+ #define DCPOMATIC_EXCEPTIONS_H
+
/** @file src/exceptions.h
* @brief Our exceptions.
*/
#include <stdexcept>
- #include <sstream>
#include <cstring>
+ #include <boost/exception/all.hpp>
+ #include <boost/filesystem.hpp>
+ #include <boost/thread.hpp>
+ extern "C" {
+ #include <libavutil/pixfmt.h>
+ }
/** @class StringError
* @brief A parent class for exceptions using messages held in a std::string
/** @param m Error message.
* @param f Name of the file that this exception concerns.
*/
- FileError (std::string m, std::string f)
+ FileError (std::string m, boost::filesystem::path f)
: StringError (m)
, _file (f)
{}
virtual ~FileError () throw () {}
/** @return name of the file that this exception concerns */
- std::string file () const {
+ boost::filesystem::path file () const {
return _file;
}
private:
/** name of the file that this exception concerns */
- std::string _file;
+ boost::filesystem::path _file;
};
{
public:
/** @param f File that we were trying to open */
- OpenFileError (std::string f)
- : FileError ("could not open file " + f, f)
- {}
+ OpenFileError (boost::filesystem::path f);
};
/** @class CreateFileError.
{
public:
/** @param f File that we were trying to create */
- CreateFileError (std::string f)
- : FileError ("could not create file " + f, f)
- {}
+ CreateFileError (boost::filesystem::path f);
};
/** @param f File that we were trying to read from.
* @param e errno value, or 0.
*/
- ReadFileError (std::string f, int e = 0)
- : FileError ("", f)
- {
- std::stringstream s;
- s << "could not read from file " << f;
- if (e) {
- s << " (" << strerror (e) << ")";
- }
- _what = s.str ();
- }
+ ReadFileError (boost::filesystem::path f, int e = 0);
};
/** @class WriteFileError.
/** @param f File that we were trying to write to.
* @param e errno value, or 0.
*/
- WriteFileError (std::string f, int e)
- : FileError ("", f)
- {
- std::stringstream s;
- s << "could not write to file " << f;
- if (e) {
- s << " (" << strerror (e) << ")";
- }
- _what = s.str ();
- }
+ WriteFileError (boost::filesystem::path f, int e);
};
/** @class SettingError.
{
public:
/** @param s Name of setting that was required */
- MissingSettingError (std::string s)
- : SettingError (s, "missing required setting " + s)
- {}
+ MissingSettingError (std::string s);
};
/** @class BadSettingError
{}
};
+class KDMError : public StringError
+{
+public:
+ KDMError (std::string s)
+ : StringError (s)
+ {}
+};
++
+ class PixelFormatError : public StringError
+ {
+ public:
+ PixelFormatError (std::string o, AVPixelFormat f);
+ };
+
+ class ExceptionStore
+ {
+ public:
+ bool thrown () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _exception;
+ }
+
+ void rethrow () {
+ boost::mutex::scoped_lock lm (_mutex);
+ boost::rethrow_exception (_exception);
+ }
+
+ protected:
+
+ void store_current () {
+ boost::mutex::scoped_lock lm (_mutex);
+ _exception = boost::current_exception ();
+ }
+
+ private:
+ boost::exception_ptr _exception;
+ mutable boost::mutex _mutex;
+ };
+
+
+
+ #endif
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2013 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/lexical_cast.hpp>
#include <boost/date_time.hpp>
#include <libxml++/libxml++.h>
- #include <libdcp/certificates.h>
- #include "cinema.h"
+ #include <libcxml/cxml.h>
+#include <libdcp/crypt_chain.h>
++#include <libdcp/cpl.h>
#include "film.h"
- #include "format.h"
#include "job.h"
- #include "filter.h"
- #include "transcoder.h"
#include "util.h"
#include "job_manager.h"
- #include "ab_transcode_job.h"
#include "transcode_job.h"
#include "scp_dcp_job.h"
- #include "make_dcp_job.h"
#include "log.h"
- #include "options.h"
#include "exceptions.h"
#include "examine_content_job.h"
#include "scaler.h"
- #include "decoder_factory.h"
#include "config.h"
- #include "check_hashes_job.h"
#include "version.h"
#include "ui_signaller.h"
- #include "video_decoder.h"
- #include "audio_decoder.h"
- #include "external_audio_decoder.h"
+ #include "playlist.h"
+ #include "player.h"
+ #include "dcp_content_type.h"
+ #include "ratio.h"
+ #include "cross.h"
++#include "cinema.h"
+
+ #include "i18n.h"
using std::string;
using std::stringstream;
using std::setfill;
using std::min;
using std::make_pair;
- using std::list;
+ using std::endl;
using std::cout;
+ using std::list;
using boost::shared_ptr;
+ using boost::weak_ptr;
using boost::lexical_cast;
+ using boost::dynamic_pointer_cast;
using boost::to_upper_copy;
using boost::ends_with;
using boost::starts_with;
using boost::optional;
+ using libdcp::Size;
- int const Film::state_version = 1;
+ int const Film::state_version = 4;
- /** Construct a Film object in a given directory, reading any metadata
- * file that exists in that directory. An exception will be thrown if
- * must_exist is true and the specified directory does not exist.
+ /** Construct a Film object in a given directory.
*
- * @param d Film directory.
- * @param must_exist true to throw an exception if does not exist.
+ * @param dir Film directory.
*/
- Film::Film (string d, bool must_exist)
- : _use_dci_name (true)
- , _trust_content_header (true)
- , _dcp_content_type (0)
- , _format (0)
+ Film::Film (boost::filesystem::path dir)
+ : _playlist (new Playlist)
+ , _use_dci_name (true)
+ , _dcp_content_type (Config::instance()->default_dcp_content_type ())
+ , _container (Config::instance()->default_container ())
+ , _resolution (RESOLUTION_2K)
, _scaler (Scaler::from_id ("bicubic"))
- , _dcp_trim_start (0)
- , _dcp_trim_end (0)
- , _dcp_ab (false)
- , _use_content_audio (true)
- , _audio_gain (0)
- , _audio_delay (0)
- , _still_duration (10)
, _with_subtitles (false)
- , _subtitle_offset (0)
- , _subtitle_scale (1)
+ , _encrypted (false)
- , _colour_lut (0)
- , _j2k_bandwidth (200000000)
- , _frames_per_second (0)
+ , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
+ , _dci_metadata (Config::instance()->default_dci_metadata ())
+ , _video_frame_rate (24)
+ , _audio_channels (MAX_AUDIO_CHANNELS)
+ , _three_d (false)
+ , _sequence_video (true)
+ , _interop (false)
, _dirty (false)
{
+ set_dci_date_today ();
+
+ _playlist->Changed.connect (bind (&Film::playlist_changed, this));
+ _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
+
/* Make state.directory a complete path without ..s (where possible)
(Code swiped from Adam Bowen on stackoverflow)
*/
- boost::filesystem::path p (boost::filesystem::system_complete (d));
+ boost::filesystem::path p (boost::filesystem::system_complete (dir));
boost::filesystem::path result;
for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
if (*i == "..") {
}
set_directory (result.string ());
-
- if (!boost::filesystem::exists (directory())) {
- if (must_exist) {
- throw OpenFileError (directory());
- } else {
- boost::filesystem::create_directory (directory());
- }
- }
+ _log.reset (new FileLog (file ("log")));
- _external_audio_stream = ExternalAudioStream::create ();
-
- if (must_exist) {
- read_metadata ();
- }
-
- _log = new FileLog (file ("log"));
- set_dci_date_today ();
+ _playlist->set_sequence_video (_sequence_video);
}
- Film::Film (Film const & o)
- : boost::enable_shared_from_this<Film> (o)
- , _log (0)
- , _directory (o._directory)
- , _name (o._name)
- , _use_dci_name (o._use_dci_name)
- , _content (o._content)
- , _trust_content_header (o._trust_content_header)
- , _dcp_content_type (o._dcp_content_type)
- , _format (o._format)
- , _crop (o._crop)
- , _filters (o._filters)
- , _scaler (o._scaler)
- , _dcp_trim_start (o._dcp_trim_start)
- , _dcp_trim_end (o._dcp_trim_end)
- , _reel_size (o._reel_size)
- , _dcp_ab (o._dcp_ab)
- , _content_audio_stream (o._content_audio_stream)
- , _external_audio (o._external_audio)
- , _use_content_audio (o._use_content_audio)
- , _audio_gain (o._audio_gain)
- , _audio_delay (o._audio_delay)
- , _still_duration (o._still_duration)
- , _subtitle_stream (o._subtitle_stream)
- , _with_subtitles (o._with_subtitles)
- , _subtitle_offset (o._subtitle_offset)
- , _subtitle_scale (o._subtitle_scale)
- , _encrypted (o._encrypted)
- , _colour_lut (o._colour_lut)
- , _j2k_bandwidth (o._j2k_bandwidth)
- , _audio_language (o._audio_language)
- , _subtitle_language (o._subtitle_language)
- , _territory (o._territory)
- , _rating (o._rating)
- , _studio (o._studio)
- , _facility (o._facility)
- , _package_type (o._package_type)
- , _size (o._size)
- , _length (o._length)
- , _content_digest (o._content_digest)
- , _content_audio_streams (o._content_audio_streams)
- , _external_audio_stream (o._external_audio_stream)
- , _subtitle_streams (o._subtitle_streams)
- , _frames_per_second (o._frames_per_second)
- , _dirty (o._dirty)
+ string
+ Film::video_identifier () const
{
+ assert (container ());
+ LocaleGuard lg;
- }
+ stringstream s;
+ s << container()->id()
+ << "_" << resolution_to_string (_resolution)
+ << "_" << _playlist->video_identifier()
+ << "_" << _video_frame_rate
+ << "_" << scaler()->id()
+ << "_" << j2k_bandwidth();
- Film::~Film ()
- {
- delete _log;
+ if (_interop) {
+ s << "_I";
+ } else {
+ s << "_S";
+ }
+
+ if (_three_d) {
+ s << "_3D";
+ }
+
+ return s.str ();
}
- /** @return The path to the directory to write JPEG2000 files to */
+ /** @return The path to the directory to write video frame info files to */
string
- Film::j2k_dir () const
+ Film::info_dir () const
{
- assert (format());
-
boost::filesystem::path p;
+ p /= "info";
+ p /= video_identifier ();
+ return dir (p.string());
+ }
- /* Start with j2c */
- p /= "j2c";
+ string
+ Film::internal_video_mxf_dir () const
+ {
+ return dir ("video");
+ }
- pair<string, string> f = Filter::ffmpeg_strings (filters());
+ string
+ Film::internal_video_mxf_filename () const
+ {
+ return video_identifier() + ".mxf";
+ }
- /* Write stuff to specify the filter / post-processing settings that are in use,
- so that we don't get confused about J2K files generated using different
- settings.
- */
- stringstream s;
- s << format()->id()
- << "_" << content_digest()
- << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
- << "_" << f.first << "_" << f.second
- << "_" << scaler()->id()
- << "_" << j2k_bandwidth()
- << "_" << boost::lexical_cast<int> (colour_lut());
+ string
+ Film::video_mxf_filename () const
+ {
+ return filename_safe_name() + "_video.mxf";
+ }
- p /= s.str ();
+ string
+ Film::audio_mxf_filename () const
+ {
+ return filename_safe_name() + "_audio.mxf";
+ }
- /* Similarly for the A/B case */
- if (dcp_ab()) {
- stringstream s;
- pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
- s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
- p /= s.str ();
+ string
+ Film::filename_safe_name () const
+ {
+ string const n = name ();
+ string o;
+ for (size_t i = 0; i < n.length(); ++i) {
+ if (isalnum (n[i])) {
+ o += n[i];
+ } else {
+ o += "_";
+ }
}
-
- return dir (p.string());
+
+ return o;
}
- /** Add suitable Jobs to the JobManager to create a DCP for this Film.
- * @param true to transcode, false to use the WAV and J2K files that are already there.
- */
+ boost::filesystem::path
+ Film::audio_analysis_path (shared_ptr<const AudioContent> c) const
+ {
+ boost::filesystem::path p = dir ("analysis");
+ p /= c->digest();
+ return p;
+ }
+
+ /** Add suitable Jobs to the JobManager to create a DCP for this Film */
void
- Film::make_dcp (bool transcode)
+ Film::make_dcp ()
{
set_dci_date_today ();
if (dcp_name().find ("/") != string::npos) {
- throw BadSettingError ("name", "cannot contain slashes");
+ throw BadSettingError (_("name"), _("cannot contain slashes"));
}
- log()->log (String::compose ("DVD-o-matic %1 git %2 using %3", dvdomatic_version, dvdomatic_git_commit, dependency_version_summary()));
+ log()->log (String::compose ("DCP-o-matic %1 git %2 using %3", dcpomatic_version, dcpomatic_git_commit, dependency_version_summary()));
{
char buffer[128];
gethostname (buffer, sizeof (buffer));
log()->log (String::compose ("Starting to make DCP on %1", buffer));
}
-
- log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? "still" : "video")));
- log()->log (String::compose ("Content length %1", length().get()));
- log()->log (String::compose ("Content digest %1", content_digest()));
+
+ ContentList cl = content ();
+ for (ContentList::const_iterator i = cl.begin(); i != cl.end(); ++i) {
+ log()->log (String::compose ("Content: %1", (*i)->technical_summary()));
+ }
+ log()->log (String::compose ("DCP video rate %1 fps", video_frame_rate()));
log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads()));
log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth()));
- #ifdef DVDOMATIC_DEBUG
- log()->log ("DVD-o-matic built in debug mode.");
+ #ifdef DCPOMATIC_DEBUG
+ log()->log ("DCP-o-matic built in debug mode.");
#else
- log()->log ("DVD-o-matic built in optimised mode.");
+ log()->log ("DCP-o-matic built in optimised mode.");
#endif
#ifdef LIBDCP_DEBUG
log()->log ("libdcp built in debug mode.");
#else
log()->log ("libdcp built in optimised mode.");
#endif
- pair<string, int> const c = cpu_info ();
- log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second));
+ log()->log (String::compose ("CPU: %1, %2 processors", cpu_info(), boost::thread::hardware_concurrency ()));
+ list<pair<string, string> > const m = mount_info ();
+ for (list<pair<string, string> >::const_iterator i = m.begin(); i != m.end(); ++i) {
+ log()->log (String::compose ("Mount: %1 %2", i->first, i->second));
+ }
- if (format() == 0) {
- throw MissingSettingError ("format");
+ if (container() == 0) {
+ throw MissingSettingError (_("container"));
}
- if (content().empty ()) {
- throw MissingSettingError ("content");
+ if (content().empty()) {
+ throw StringError (_("You must add some content to the DCP before creating it"));
}
if (dcp_content_type() == 0) {
- throw MissingSettingError ("content type");
+ throw MissingSettingError (_("content type"));
}
if (name().empty()) {
- throw MissingSettingError ("name");
- }
-
- shared_ptr<EncodeOptions> oe (new EncodeOptions (j2k_dir(), ".j2c", dir ("wavs")));
- oe->out_size = format()->dcp_size ();
- oe->padding = format()->dcp_padding (shared_from_this ());
- if (dcp_length ()) {
- oe->video_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get());
- if (audio_stream()) {
- oe->audio_range = make_pair (
-
- video_frames_to_audio_frames (
- oe->video_range.get().first,
- dcp_audio_sample_rate (audio_stream()->sample_rate()),
- dcp_frame_rate (frames_per_second()).frames_per_second
- ),
-
- video_frames_to_audio_frames (
- oe->video_range.get().second,
- dcp_audio_sample_rate (audio_stream()->sample_rate()),
- dcp_frame_rate (frames_per_second()).frames_per_second
- )
- );
- }
-
- }
-
- oe->video_skip = dcp_frame_rate (frames_per_second()).skip;
-
- shared_ptr<DecodeOptions> od (new DecodeOptions);
- od->decode_subtitles = with_subtitles ();
-
- shared_ptr<Job> r;
-
- if (transcode) {
- if (dcp_ab()) {
- r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
- } else {
- r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
- }
+ throw MissingSettingError (_("name"));
}
- r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), od, oe, r)));
- JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), oe, r)));
- }
-
- /** Start a job to examine our content file */
- void
- Film::examine_content ()
- {
- if (_examine_content_job) {
- return;
- }
-
- _examine_content_job.reset (new ExamineContentJob (shared_from_this(), shared_ptr<Job> ()));
- _examine_content_job->Finished.connect (bind (&Film::examine_content_finished, this));
- JobManager::instance()->add (_examine_content_job);
- }
-
- void
- Film::examine_content_finished ()
- {
- _examine_content_job.reset ();
+ JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
}
/** Start a job to send our DCP to the configured TMS */
void
Film::send_dcp_to_tms ()
{
- shared_ptr<Job> j (new SCPDCPJob (shared_from_this(), shared_ptr<Job> ()));
+ shared_ptr<Job> j (new SCPDCPJob (shared_from_this()));
JobManager::instance()->add (j);
}
int
Film::encoded_frames () const
{
- if (format() == 0) {
+ if (container() == 0) {
return 0;
}
int N = 0;
- for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (j2k_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
+ for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (info_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
++N;
boost::this_thread::interruption_point ();
}
void
Film::write_metadata () const
{
- boost::mutex::scoped_lock lm (_state_mutex);
+ if (!boost::filesystem::exists (directory())) {
+ boost::filesystem::create_directory (directory());
+ }
+
+ LocaleGuard lg;
boost::filesystem::create_directories (directory());
- string const m = file ("metadata");
- ofstream f (m.c_str ());
- if (!f.good ()) {
- throw CreateFileError (m);
- }
+ xmlpp::Document doc;
+ xmlpp::Element* root = doc.create_root_node ("Metadata");
- f << "version " << state_version << "\n";
+ root->add_child("Version")->add_child_text (lexical_cast<string> (state_version));
+ root->add_child("Name")->add_child_text (_name);
+ root->add_child("UseDCIName")->add_child_text (_use_dci_name ? "1" : "0");
- /* User stuff */
- f << "name " << _name << "\n";
- f << "use_dci_name " << _use_dci_name << "\n";
- f << "content " << _content << "\n";
- f << "trust_content_header " << (_trust_content_header ? "1" : "0") << "\n";
if (_dcp_content_type) {
- f << "dcp_content_type " << _dcp_content_type->pretty_name () << "\n";
- }
- if (_format) {
- f << "format " << _format->as_metadata () << "\n";
- }
- f << "left_crop " << _crop.left << "\n";
- f << "right_crop " << _crop.right << "\n";
- f << "top_crop " << _crop.top << "\n";
- f << "bottom_crop " << _crop.bottom << "\n";
- for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
- f << "filter " << (*i)->id () << "\n";
- }
- f << "scaler " << _scaler->id () << "\n";
- f << "dcp_trim_start " << _dcp_trim_start << "\n";
- f << "dcp_trim_end " << _dcp_trim_end << "\n";
- if (_reel_size) {
- f << "reel_size " << _reel_size.get() << "\n";
- }
- f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
- if (_content_audio_stream) {
- f << "selected_content_audio_stream " << _content_audio_stream->to_string() << "\n";
- }
- for (vector<string>::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) {
- f << "external_audio " << *i << "\n";
- }
- f << "use_content_audio " << (_use_content_audio ? "1" : "0") << "\n";
- f << "audio_gain " << _audio_gain << "\n";
- f << "audio_delay " << _audio_delay << "\n";
- f << "still_duration " << _still_duration << "\n";
- if (_subtitle_stream) {
- f << "selected_subtitle_stream " << _subtitle_stream->to_string() << "\n";
+ root->add_child("DCPContentType")->add_child_text (_dcp_content_type->dci_name ());
}
- f << "with_subtitles " << _with_subtitles << "\n";
- f << "subtitle_offset " << _subtitle_offset << "\n";
- f << "subtitle_scale " << _subtitle_scale << "\n";
- f << "encrypted " << _encrypted << "\n";
- f << "colour_lut " << _colour_lut << "\n";
- f << "j2k_bandwidth " << _j2k_bandwidth << "\n";
- f << "audio_language " << _audio_language << "\n";
- f << "subtitle_language " << _subtitle_language << "\n";
- f << "territory " << _territory << "\n";
- f << "rating " << _rating << "\n";
- f << "studio " << _studio << "\n";
- f << "facility " << _facility << "\n";
- f << "package_type " << _package_type << "\n";
-
- f << "width " << _size.width << "\n";
- f << "height " << _size.height << "\n";
- f << "length " << _length.get_value_or(0) << "\n";
- f << "content_digest " << _content_digest << "\n";
-
- for (vector<shared_ptr<AudioStream> >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
- f << "content_audio_stream " << (*i)->to_string () << "\n";
- }
-
- f << "external_audio_stream " << _external_audio_stream->to_string() << "\n";
- for (vector<shared_ptr<SubtitleStream> >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
- f << "subtitle_stream " << (*i)->to_string () << "\n";
+ if (_container) {
+ root->add_child("Container")->add_child_text (_container->id ());
}
- f << "frames_per_second " << _frames_per_second << "\n";
+ 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 (lexical_cast<string> (_j2k_bandwidth));
+ _dci_metadata.as_xml (root->add_child ("DCIMetadata"));
+ root->add_child("VideoFrameRate")->add_child_text (lexical_cast<string> (_video_frame_rate));
+ root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
+ root->add_child("AudioChannels")->add_child_text (lexical_cast<string> (_audio_channels));
+ 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("Encrypted")->add_child_text (_encrypted ? "1" : "0");
+ _playlist->as_xml (root->add_child ("Playlist"));
+
+ doc.write_to_file_formatted (file ("metadata.xml"));
_dirty = false;
}
void
Film::read_metadata ()
{
- boost::mutex::scoped_lock lm (_state_mutex);
-
- _external_audio.clear ();
- _content_audio_streams.clear ();
- _subtitle_streams.clear ();
-
- boost::optional<int> version;
-
- /* Backward compatibility things */
- boost::optional<int> audio_sample_rate;
- boost::optional<int> audio_stream_index;
- boost::optional<int> subtitle_stream_index;
+ LocaleGuard lg;
- ifstream f (file ("metadata").c_str());
- if (!f.good()) {
- throw OpenFileError (file("metadata"));
+ if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
+ throw StringError (_("This film was created with an older version of DCP-o-matic, and unfortunately it cannot be loaded into this version. You will need to create a new Film, re-add your content and set it up again. Sorry!"));
}
-
- multimap<string, string> kv = read_key_value (f);
- /* We need version before anything else */
- multimap<string, string>::iterator v = kv.find ("version");
- if (v != kv.end ()) {
- version = atoi (v->second.c_str());
- }
+ cxml::Document f ("Metadata");
+ f.read_file (file ("metadata.xml"));
- for (multimap<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
- string const k = i->first;
- string const v = i->second;
+ _name = f.string_child ("Name");
+ _use_dci_name = f.bool_child ("UseDCIName");
- if (k == "audio_sample_rate") {
- audio_sample_rate = atoi (v.c_str());
+ {
+ optional<string> c = f.optional_string_child ("DCPContentType");
+ if (c) {
+ _dcp_content_type = DCPContentType::from_dci_name (c.get ());
}
+ }
- /* User-specified stuff */
- if (k == "name") {
- _name = v;
- } else if (k == "use_dci_name") {
- _use_dci_name = (v == "1");
- } else if (k == "content") {
- _content = v;
- } else if (k == "trust_content_header") {
- _trust_content_header = (v == "1");
- } else if (k == "dcp_content_type") {
- _dcp_content_type = DCPContentType::from_pretty_name (v);
- } else if (k == "format") {
- _format = Format::from_metadata (v);
- } else if (k == "left_crop") {
- _crop.left = atoi (v.c_str ());
- } else if (k == "right_crop") {
- _crop.right = atoi (v.c_str ());
- } else if (k == "top_crop") {
- _crop.top = atoi (v.c_str ());
- } else if (k == "bottom_crop") {
- _crop.bottom = atoi (v.c_str ());
- } else if (k == "filter") {
- _filters.push_back (Filter::from_id (v));
- } else if (k == "scaler") {
- _scaler = Scaler::from_id (v);
- } else if (k == "dcp_trim_start") {
- _dcp_trim_start = atoi (v.c_str ());
- } else if (k == "dcp_trim_end") {
- _dcp_trim_end = atoi (v.c_str ());
- } else if (k == "reel_size") {
- _reel_size = boost::lexical_cast<uint64_t> (v);
- } else if (k == "dcp_ab") {
- _dcp_ab = (v == "1");
- } else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) {
- if (!version) {
- audio_stream_index = atoi (v.c_str ());
- } else {
- _content_audio_stream = audio_stream_factory (v, version);
- }
- } else if (k == "external_audio") {
- _external_audio.push_back (v);
- } else if (k == "use_content_audio") {
- _use_content_audio = (v == "1");
- } else if (k == "audio_gain") {
- _audio_gain = atof (v.c_str ());
- } else if (k == "audio_delay") {
- _audio_delay = atoi (v.c_str ());
- } else if (k == "still_duration") {
- _still_duration = atoi (v.c_str ());
- } else if (k == "selected_subtitle_stream") {
- if (!version) {
- subtitle_stream_index = atoi (v.c_str ());
- } else {
- _subtitle_stream = subtitle_stream_factory (v, version);
- }
- } else if (k == "with_subtitles") {
- _with_subtitles = (v == "1");
- } else if (k == "subtitle_offset") {
- _subtitle_offset = atoi (v.c_str ());
- } else if (k == "subtitle_scale") {
- _subtitle_scale = atof (v.c_str ());
- } else if (k == "encrypted") {
- _encrypted = (v == "1");
- } else if (k == "colour_lut") {
- _colour_lut = atoi (v.c_str ());
- } else if (k == "j2k_bandwidth") {
- _j2k_bandwidth = atoi (v.c_str ());
- } else if (k == "audio_language") {
- _audio_language = v;
- } else if (k == "subtitle_language") {
- _subtitle_language = v;
- } else if (k == "territory") {
- _territory = v;
- } else if (k == "rating") {
- _rating = v;
- } else if (k == "studio") {
- _studio = v;
- } else if (k == "facility") {
- _facility = v;
- } else if (k == "package_type") {
- _package_type = v;
- }
-
- /* Cached stuff */
- if (k == "width") {
- _size.width = atoi (v.c_str ());
- } else if (k == "height") {
- _size.height = atoi (v.c_str ());
- } else if (k == "length") {
- int const vv = atoi (v.c_str ());
- if (vv) {
- _length = vv;
- }
- } else if (k == "content_digest") {
- _content_digest = v;
- } else if (k == "content_audio_stream" || (!version && k == "audio_stream")) {
- _content_audio_streams.push_back (audio_stream_factory (v, version));
- } else if (k == "external_audio_stream") {
- _external_audio_stream = audio_stream_factory (v, version);
- } else if (k == "subtitle_stream") {
- _subtitle_streams.push_back (subtitle_stream_factory (v, version));
- } else if (k == "frames_per_second") {
- _frames_per_second = atof (v.c_str ());
+ {
+ optional<string> c = f.optional_string_child ("Container");
+ if (c) {
+ _container = Ratio::from_id (c.get ());
}
}
- if (!version) {
- if (audio_sample_rate) {
- /* version < 1 didn't specify sample rate in the audio streams, so fill it in here */
- for (vector<shared_ptr<AudioStream> >::iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
- (*i)->set_sample_rate (audio_sample_rate.get());
- }
- }
+ _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");
+ _dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
+ _video_frame_rate = f.number_child<int> ("VideoFrameRate");
+ _dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
+ _audio_channels = f.number_child<int> ("AudioChannels");
+ _sequence_video = f.bool_child ("SequenceVideo");
+ _three_d = f.bool_child ("ThreeD");
+ _interop = f.bool_child ("Interop");
- /* also the selected stream was specified as an index */
- if (audio_stream_index && audio_stream_index.get() >= 0 && audio_stream_index.get() < (int) _content_audio_streams.size()) {
- _content_audio_stream = _content_audio_streams[audio_stream_index.get()];
- }
+ _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"));
- /* similarly the subtitle */
- if (subtitle_stream_index && subtitle_stream_index.get() >= 0 && subtitle_stream_index.get() < (int) _subtitle_streams.size()) {
- _subtitle_stream = _subtitle_streams[subtitle_stream_index.get()];
- }
- }
-
_dirty = false;
}
- Size
- Film::cropped_size (Size s) const
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- s.width -= _crop.left + _crop.right;
- s.height -= _crop.top + _crop.bottom;
- return s;
- }
-
/** Given a directory name, return its full path within the Film's directory.
* The directory (and its parents) will be created if they do not exist.
*/
string
Film::dir (string d) const
{
- boost::mutex::scoped_lock lm (_directory_mutex);
boost::filesystem::path p;
p /= _directory;
p /= d;
+
boost::filesystem::create_directories (p);
+
return p.string ();
}
/** Given a file or directory name, return its full path within the Film's directory.
- * _directory_mutex must not be locked on entry.
+ * Any required parent directories will be created.
*/
string
Film::file (string f) const
{
- boost::mutex::scoped_lock lm (_directory_mutex);
boost::filesystem::path p;
p /= _directory;
p /= f;
- return p.string ();
- }
-
- /** @return full path of the content (actual video) file
- * of the Film.
- */
- string
- Film::content_path () const
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- if (boost::filesystem::path(_content).has_root_directory ()) {
- return _content;
- }
-
- return file (_content);
- }
- ContentType
- Film::content_type () const
- {
- if (boost::filesystem::is_directory (_content)) {
- /* Directory of images, we assume */
- return VIDEO;
- }
-
- if (still_image_file (_content)) {
- return STILL;
- }
-
- return VIDEO;
- }
-
- /** @return The sampling rate that we will resample the audio to */
- int
- Film::target_audio_sample_rate () const
- {
- if (!audio_stream()) {
- return 0;
- }
+ boost::filesystem::create_directories (p.parent_path ());
- /* Resample to a DCI-approved sample rate */
- double t = dcp_audio_sample_rate (audio_stream()->sample_rate());
-
- DCPFrameRate dfr = dcp_frame_rate (frames_per_second ());
-
- /* Compensate for the fact that video will be rounded to the
- nearest integer number of frames per second.
- */
- if (dfr.run_fast) {
- t *= _frames_per_second * dfr.skip / dfr.frames_per_second;
- }
-
- return rint (t);
- }
-
- boost::optional<int>
- Film::dcp_length () const
- {
- if (content_type() == STILL) {
- return _still_duration * frames_per_second();
- }
-
- if (!length()) {
- return boost::optional<int> ();
- }
-
- return length().get() - dcp_trim_start() - dcp_trim_end();
+ return p.string ();
}
/** @return a DCI-compliant name for a DCP of this film */
string
- Film::dci_name () const
+ Film::dci_name (bool if_created_now) const
{
stringstream d;
fixed_name = fixed_name.substr (0, 14);
}
- d << fixed_name << "_";
+ d << fixed_name;
if (dcp_content_type()) {
- d << dcp_content_type()->dci_name() << "_";
+ d << "_" << dcp_content_type()->dci_name();
+ d << "-" << dci_metadata().content_version;
+ }
+
+ if (three_d ()) {
+ d << "-3D";
+ }
+
+ if (video_frame_rate() != 24) {
+ d << "-" << video_frame_rate();
}
- if (format()) {
- d << format()->dci_name() << "_";
+ if (container()) {
+ d << "_" << container()->dci_name();
}
- if (!audio_language().empty ()) {
- d << audio_language();
- if (!subtitle_language().empty() && with_subtitles()) {
- d << "-" << subtitle_language();
+ DCIMetadata const dm = dci_metadata ();
+
+ if (!dm.audio_language.empty ()) {
+ d << "_" << dm.audio_language;
+ if (!dm.subtitle_language.empty() && with_subtitles()) {
+ d << "-" << dm.subtitle_language;
} else {
d << "-XX";
}
-
- d << "_";
}
- if (!territory().empty ()) {
- d << territory();
- if (!rating().empty ()) {
- d << "-" << rating();
+ if (!dm.territory.empty ()) {
+ d << "_" << dm.territory;
+ if (!dm.rating.empty ()) {
+ d << "-" << dm.rating;
}
- d << "_";
}
- switch (audio_channels()) {
+ switch (audio_channels ()) {
case 1:
- d << "10_";
+ d << "_10";
break;
case 2:
- d << "20_";
+ d << "_20";
break;
- case 6:
- d << "51_";
+ case 3:
+ d << "_30";
+ break;
+ case 4:
+ d << "_40";
break;
- case 8:
- d << "71_";
+ case 5:
+ d << "_50";
+ break;
+ case 6:
+ d << "_51";
break;
}
- d << "2K_";
+ d << "_" << resolution_to_string (_resolution);
- if (!studio().empty ()) {
- d << studio() << "_";
+ if (!dm.studio.empty ()) {
+ d << "_" << dm.studio;
}
- d << boost::gregorian::to_iso_string (_dci_date) << "_";
+ if (if_created_now) {
+ d << "_" << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
+ } else {
+ d << "_" << boost::gregorian::to_iso_string (_dci_date);
+ }
- if (!facility().empty ()) {
- d << facility() << "_";
+ if (!dm.facility.empty ()) {
+ d << "_" << dm.facility;
}
- if (!package_type().empty ()) {
- d << package_type();
+ if (!dm.package_type.empty ()) {
+ d << "_" << dm.package_type;
}
return d.str ();
/** @return name to give the DCP */
string
- Film::dcp_name () const
+ Film::dcp_name (bool if_created_now) const
{
if (use_dci_name()) {
- return dci_name ();
+ return dci_name (if_created_now);
}
return name();
void
Film::set_directory (string d)
{
- boost::mutex::scoped_lock lm (_state_mutex);
_directory = d;
_dirty = true;
}
void
Film::set_name (string n)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _name = n;
- }
+ _name = n;
signal_changed (NAME);
}
void
Film::set_use_dci_name (bool u)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _use_dci_name = u;
- }
+ _use_dci_name = u;
signal_changed (USE_DCI_NAME);
}
- void
- Film::set_content (string c)
- {
- string check = directory ();
-
- #if BOOST_FILESYSTEM_VERSION == 3
- boost::filesystem::path slash ("/");
- string platform_slash = slash.make_preferred().string ();
- #else
- #ifdef DVDOMATIC_WINDOWS
- string platform_slash = "\\";
- #else
- string platform_slash = "/";
- #endif
- #endif
-
- if (!ends_with (check, platform_slash)) {
- check += platform_slash;
- }
-
- if (boost::filesystem::path(c).has_root_directory () && starts_with (c, check)) {
- c = c.substr (_directory.length() + 1);
- }
-
- string old_content;
-
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- if (c == _content) {
- return;
- }
-
- old_content = _content;
- _content = c;
- }
-
- /* Reset streams here in case the new content doesn't have one or the other */
- _content_audio_stream = shared_ptr<AudioStream> ();
- _subtitle_stream = shared_ptr<SubtitleStream> ();
-
- /* Start off using content audio */
- set_use_content_audio (true);
-
- /* Create a temporary decoder so that we can get information
- about the content.
- */
-
- try {
- shared_ptr<DecodeOptions> o (new DecodeOptions);
- Decoders d = decoder_factory (shared_from_this(), o, 0);
-
- set_size (d.video->native_size ());
- set_frames_per_second (d.video->frames_per_second ());
- set_subtitle_streams (d.video->subtitle_streams ());
- if (d.audio) {
- set_content_audio_streams (d.audio->audio_streams ());
- }
-
- /* Start off with the first audio and subtitle streams */
- if (d.audio && !d.audio->audio_streams().empty()) {
- set_content_audio_stream (d.audio->audio_streams().front());
- }
-
- if (!d.video->subtitle_streams().empty()) {
- set_subtitle_stream (d.video->subtitle_streams().front());
- }
-
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _content = c;
- }
-
- signal_changed (CONTENT);
-
- examine_content ();
-
- } catch (...) {
-
- boost::mutex::scoped_lock lm (_state_mutex);
- _content = old_content;
- throw;
-
- }
-
- /* Default format */
- switch (content_type()) {
- case STILL:
- set_format (Format::from_id ("var-185"));
- break;
- case VIDEO:
- set_format (Format::from_id ("185"));
- break;
- }
-
- /* Still image DCPs must use external audio */
- if (content_type() == STILL) {
- set_use_content_audio (false);
- }
- }
-
- void
- Film::set_trust_content_header (bool t)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _trust_content_header = t;
- }
-
- signal_changed (TRUST_CONTENT_HEADER);
-
- if (!_trust_content_header && !content().empty()) {
- /* We just said that we don't trust the content's header */
- examine_content ();
- }
- }
-
void
Film::set_dcp_content_type (DCPContentType const * t)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _dcp_content_type = t;
- }
+ _dcp_content_type = t;
signal_changed (DCP_CONTENT_TYPE);
}
void
- Film::set_format (Format const * f)
+ Film::set_container (Ratio const * c)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _format = f;
- }
- signal_changed (FORMAT);
+ _container = c;
+ signal_changed (CONTAINER);
}
void
- Film::set_crop (Crop c)
+ Film::set_resolution (Resolution r)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _crop = c;
- }
- signal_changed (CROP);
+ _resolution = r;
+ signal_changed (RESOLUTION);
}
void
- Film::set_left_crop (int c)
+ Film::set_scaler (Scaler const * s)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
-
- if (_crop.left == c) {
- return;
- }
-
- _crop.left = c;
- }
- signal_changed (CROP);
+ _scaler = s;
+ signal_changed (SCALER);
}
void
- Film::set_right_crop (int c)
+ Film::set_with_subtitles (bool w)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- if (_crop.right == c) {
- return;
- }
-
- _crop.right = c;
- }
- signal_changed (CROP);
+ _with_subtitles = w;
+ signal_changed (WITH_SUBTITLES);
}
void
- Film::set_top_crop (int c)
+ Film::set_j2k_bandwidth (int b)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- if (_crop.top == c) {
- return;
- }
-
- _crop.top = c;
- }
- signal_changed (CROP);
+ _j2k_bandwidth = b;
+ signal_changed (J2K_BANDWIDTH);
}
void
- Film::set_bottom_crop (int c)
+ Film::set_dci_metadata (DCIMetadata m)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- if (_crop.bottom == c) {
- return;
- }
-
- _crop.bottom = c;
- }
- signal_changed (CROP);
+ _dci_metadata = m;
+ signal_changed (DCI_METADATA);
}
void
- Film::set_filters (vector<Filter const *> f)
+ Film::set_video_frame_rate (int f)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _filters = f;
- }
- signal_changed (FILTERS);
+ _video_frame_rate = f;
+ signal_changed (VIDEO_FRAME_RATE);
}
void
- Film::set_scaler (Scaler const * s)
+ Film::set_audio_channels (int c)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _scaler = s;
- }
- signal_changed (SCALER);
+ _audio_channels = c;
+ signal_changed (AUDIO_CHANNELS);
}
void
- Film::set_dcp_trim_start (int t)
+ Film::set_three_d (bool t)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _dcp_trim_start = t;
- }
- signal_changed (DCP_TRIM_START);
+ _three_d = t;
+ signal_changed (THREE_D);
}
void
- Film::set_dcp_trim_end (int t)
+ Film::set_interop (bool i)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _dcp_trim_end = t;
- }
- signal_changed (DCP_TRIM_END);
+ _interop = i;
+ signal_changed (INTEROP);
}
void
- Film::set_reel_size (uint64_t s)
+ Film::signal_changed (Property p)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _reel_size = s;
- }
- signal_changed (REEL_SIZE);
- }
+ _dirty = true;
- void
- Film::unset_reel_size ()
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _reel_size = boost::optional<uint64_t> ();
+ switch (p) {
+ case Film::CONTENT:
+ set_video_frame_rate (_playlist->best_dcp_frame_rate ());
+ break;
+ case Film::VIDEO_FRAME_RATE:
+ case Film::SEQUENCE_VIDEO:
+ _playlist->maybe_sequence_video ();
+ break;
+ default:
+ break;
}
- signal_changed (REEL_SIZE);
- }
- void
- Film::set_dcp_ab (bool a)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _dcp_ab = a;
+ if (ui_signaller) {
+ ui_signaller->emit (boost::bind (boost::ref (Changed), p));
}
- signal_changed (DCP_AB);
}
void
- Film::set_content_audio_stream (shared_ptr<AudioStream> s)
+ Film::set_dci_date_today ()
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _content_audio_stream = s;
- }
- signal_changed (CONTENT_AUDIO_STREAM);
+ _dci_date = boost::gregorian::day_clock::local_day ();
}
- void
- Film::set_external_audio (vector<string> a)
+ string
+ Film::info_path (int f, Eyes e) const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _external_audio = a;
- }
+ boost::filesystem::path p;
+ p /= info_dir ();
- shared_ptr<DecodeOptions> o (new DecodeOptions);
- shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), o, 0));
- if (decoder->audio_stream()) {
- _external_audio_stream = decoder->audio_stream ();
+ stringstream s;
+ s.width (8);
+ s << setfill('0') << f;
+
+ if (e == EYES_LEFT) {
+ s << ".L";
+ } else if (e == EYES_RIGHT) {
+ s << ".R";
}
+
+ s << ".md5";
- signal_changed (EXTERNAL_AUDIO);
+ p /= s.str();
+
+ /* info_dir() will already have added any initial bit of the path,
+ so don't call file() on this.
+ */
+ return p.string ();
}
- void
- Film::set_use_content_audio (bool e)
+ string
+ Film::j2c_path (int f, Eyes e, bool t) const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _use_content_audio = e;
- }
+ boost::filesystem::path p;
+ p /= "j2c";
+ p /= video_identifier ();
- signal_changed (USE_CONTENT_AUDIO);
- }
+ stringstream s;
+ s.width (8);
+ s << setfill('0') << f;
- void
- Film::set_audio_gain (float g)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _audio_gain = g;
+ if (e == EYES_LEFT) {
+ s << ".L";
+ } else if (e == EYES_RIGHT) {
+ s << ".R";
}
- signal_changed (AUDIO_GAIN);
- }
+
+ s << ".j2c";
- void
- Film::set_audio_delay (int d)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _audio_delay = d;
+ if (t) {
+ s << ".tmp";
}
- signal_changed (AUDIO_DELAY);
- }
- void
- Film::set_still_duration (int d)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _still_duration = d;
- }
- signal_changed (STILL_DURATION);
+ p /= s.str();
+ return file (p.string ());
}
- void
- Film::set_subtitle_stream (shared_ptr<SubtitleStream> s)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_stream = s;
- }
- signal_changed (SUBTITLE_STREAM);
- }
+ /** Make an educated guess as to whether we have a complete DCP
+ * or not.
+ * @return true if we do.
+ */
- void
- Film::set_with_subtitles (bool w)
+ bool
+ Film::have_dcp () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _with_subtitles = w;
+ try {
+ libdcp::DCP dcp (dir (dcp_name()));
+ dcp.read ();
+ } catch (...) {
+ return false;
}
- signal_changed (WITH_SUBTITLES);
- }
- void
- Film::set_subtitle_offset (int o)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_offset = o;
- }
- signal_changed (SUBTITLE_OFFSET);
+ return true;
}
- void
- Film::set_subtitle_scale (float s)
+ shared_ptr<Player>
+ Film::make_player () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_scale = s;
- }
- signal_changed (SUBTITLE_SCALE);
+ return shared_ptr<Player> (new Player (shared_from_this (), _playlist));
}
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _encrypted = e;
- }
+void
+Film::set_encrypted (bool e)
+{
- void
- Film::set_colour_lut (int i)
++ _encrypted = e;
+ signal_changed (ENCRYPTED);
+}
+
+ shared_ptr<Playlist>
+ Film::playlist () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _colour_lut = i;
- }
- signal_changed (COLOUR_LUT);
+ return _playlist;
}
- void
- Film::set_j2k_bandwidth (int b)
+ ContentList
+ Film::content () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _j2k_bandwidth = b;
- }
- signal_changed (J2K_BANDWIDTH);
+ return _playlist->content ();
}
void
- Film::set_audio_language (string l)
+ Film::examine_and_add_content (shared_ptr<Content> c)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _audio_language = l;
- }
- signal_changed (DCI_METADATA);
+ shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
+ j->Finished.connect (bind (&Film::maybe_add_content, this, boost::weak_ptr<Job> (j), boost::weak_ptr<Content> (c)));
+ JobManager::instance()->add (j);
}
void
- Film::set_subtitle_language (string l)
+ Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_language = l;
+ shared_ptr<Job> job = j.lock ();
+ if (!job || !job->finished_ok ()) {
+ return;
}
- signal_changed (DCI_METADATA);
- }
-
- void
- Film::set_territory (string t)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _territory = t;
+
+ shared_ptr<Content> content = c.lock ();
+ if (content) {
+ add_content (content);
}
- signal_changed (DCI_METADATA);
}
void
- Film::set_rating (string r)
+ Film::add_content (shared_ptr<Content> c)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _rating = r;
+ /* Add video content after any existing content */
+ if (dynamic_pointer_cast<VideoContent> (c)) {
+ c->set_position (_playlist->video_end ());
}
- signal_changed (DCI_METADATA);
+
+ _playlist->add (c);
}
void
- Film::set_studio (string s)
+ Film::remove_content (shared_ptr<Content> c)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _studio = s;
- }
- signal_changed (DCI_METADATA);
+ _playlist->remove (c);
}
- void
- Film::set_facility (string f)
+ Time
+ Film::length () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _facility = f;
- }
- signal_changed (DCI_METADATA);
+ return _playlist->length ();
}
- void
- Film::set_package_type (string p)
+ bool
+ Film::has_subtitles () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _package_type = p;
- }
- signal_changed (DCI_METADATA);
+ return _playlist->has_subtitles ();
}
- void
- Film::set_size (Size s)
+ OutputVideoFrame
+ Film::best_video_frame_rate () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _size = s;
- }
- signal_changed (SIZE);
+ return _playlist->best_dcp_frame_rate ();
}
void
- Film::set_length (SourceFrame l)
+ Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _length = l;
+ if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
+ set_video_frame_rate (_playlist->best_dcp_frame_rate ());
+ }
+
+ if (ui_signaller) {
+ ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p));
}
- signal_changed (LENGTH);
}
void
- Film::unset_length ()
+ Film::playlist_changed ()
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _length = boost::none;
- }
- signal_changed (LENGTH);
+ signal_changed (CONTENT);
}
- void
- Film::set_content_digest (string d)
+ OutputAudioFrame
+ Film::time_to_audio_frames (Time t) const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _content_digest = d;
- }
- _dirty = true;
+ return t * audio_frame_rate () / TIME_HZ;
}
- void
- Film::set_content_audio_streams (vector<shared_ptr<AudioStream> > s)
+ OutputVideoFrame
+ Film::time_to_video_frames (Time t) const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _content_audio_streams = s;
- }
- signal_changed (CONTENT_AUDIO_STREAMS);
+ return t * video_frame_rate () / TIME_HZ;
}
- void
- Film::set_subtitle_streams (vector<shared_ptr<SubtitleStream> > s)
+ Time
+ Film::audio_frames_to_time (OutputAudioFrame f) const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_streams = s;
- }
- signal_changed (SUBTITLE_STREAMS);
+ return f * TIME_HZ / audio_frame_rate ();
}
- void
- Film::set_frames_per_second (float f)
+ Time
+ Film::video_frames_to_time (OutputVideoFrame f) const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _frames_per_second = f;
- }
- signal_changed (FRAMES_PER_SECOND);
- }
-
- void
- Film::signal_changed (Property p)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _dirty = true;
- }
-
- if (ui_signaller) {
- ui_signaller->emit (boost::bind (boost::ref (Changed), p));
- }
+ return f * TIME_HZ / video_frame_rate ();
}
- int
- Film::audio_channels () const
+ OutputAudioFrame
+ Film::audio_frame_rate () const
{
- shared_ptr<AudioStream> s = audio_stream ();
- if (!s) {
- return 0;
- }
-
- return s->channels ();
+ /* XXX */
+ return 48000;
}
void
- Film::set_dci_date_today ()
+ Film::set_sequence_video (bool s)
{
- _dci_date = boost::gregorian::day_clock::local_day ();
+ _sequence_video = s;
+ _playlist->set_sequence_video (s);
+ signal_changed (SEQUENCE_VIDEO);
}
- boost::shared_ptr<AudioStream>
- Film::audio_stream () const
+ libdcp::Size
+ Film::full_frame () const
{
- if (use_content_audio()) {
- return _content_audio_stream;
+ switch (_resolution) {
+ case RESOLUTION_2K:
+ return libdcp::Size (2048, 1080);
+ case RESOLUTION_4K:
+ return libdcp::Size (4096, 2160);
}
- return _external_audio_stream;
+ assert (false);
+ return libdcp::Size ();
}
- shared_ptr<xmlpp::Document> kdm = dcp.cpls().front()->make_kdm (chain, signer_key.string(), (*i)->certificate, from, until);
+
+void
+Film::make_kdms (
+ list<shared_ptr<Screen> > screens,
+ boost::posix_time::ptime from,
+ boost::posix_time::ptime until,
+ string directory
+ ) const
+{
+ string const cd = Config::instance()->crypt_chain_directory ();
+ if (boost::filesystem::is_empty (cd)) {
+ libdcp::make_crypt_chain (cd);
+ }
+
+ libdcp::CertificateChain chain;
+
+ {
+ boost::filesystem::path p (cd);
+ p /= "ca.self-signed.pem";
+ chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p.string ())));
+ }
+
+ {
+ boost::filesystem::path p (cd);
+ p /= "intermediate.signed.pem";
+ chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p.string ())));
+ }
+
+ {
+ boost::filesystem::path p (cd);
+ p /= "leaf.signed.pem";
+ chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p.string ())));
+ }
+
+ boost::filesystem::path signer_key (cd);
+ signer_key /= "leaf.key";
+
+ /* Find the DCP to make the KDM for */
+ string const dir = this->directory ();
+ list<string> dcps;
+ for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
+ if (boost::filesystem::is_directory (*i) && i->path().leaf() != "j2c" && i->path().leaf() != "wavs") {
+ dcps.push_back (i->path().string());
+ }
+ }
+
+ if (dcps.empty()) {
+ throw KDMError ("Could not find DCP to make KDM for");
+ } else if (dcps.size() > 1) {
+ throw KDMError ("More than one possible DCP to make KDM for");
+ }
+
+ for (list<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) {
+
+ libdcp::DCP dcp (dcps.front ());
+ dcp.read ();
+
+ /* XXX: single CPL only */
++ shared_ptr<xmlpp::Document> kdm = dcp.cpls().front()->make_kdm (
++ chain, signer_key.string(), (*i)->certificate, from, until, _interop, libdcp::MXFMetadata (), Config::instance()->dcp_metadata ()
++ );
+
+ boost::filesystem::path out = directory;
+ out /= "kdm.xml";
+ kdm->write_to_file_formatted (out.string());
+ }
+}
+
*/
/** @file src/film.h
- * @brief A representation of a piece of video (with sound), including naming,
- * the source content file, and how it should be presented in a DCP.
+ * @brief A representation of some audio and video content, and details of
+ * how they should be presented in a DCP.
*/
- #ifndef DVDOMATIC_FILM_H
- #define DVDOMATIC_FILM_H
+ #ifndef DCPOMATIC_FILM_H
+ #define DCPOMATIC_FILM_H
#include <string>
#include <vector>
#include <inttypes.h>
- #include <boost/thread/mutex.hpp>
- #include <boost/thread.hpp>
#include <boost/signals2.hpp>
#include <boost/enable_shared_from_this.hpp>
- #include <boost/date_time/posix_time/posix_time.hpp>
- extern "C" {
- #include <libavcodec/avcodec.h>
- }
- #include "dcp_content_type.h"
+ #include <boost/filesystem.hpp>
#include "util.h"
- #include "stream.h"
+ #include "types.h"
+ #include "dci_metadata.h"
- class Format;
- class Job;
- class Filter;
+ class DCPContentType;
class Log;
- class ExamineContentJob;
- class ExternalAudioStream;
+ class Content;
+ class Player;
+ class Playlist;
+ class AudioContent;
+ class Scaler;
+class Screen;
/** @class Film
- * @brief A representation of a video, maybe with sound.
*
- * A representation of a piece of video (maybe with sound), including naming,
- * the source content file, and how it should be presented in a DCP.
+ * @brief A representation of some audio and video content, and details of
+ * how they should be presented in a DCP.
+ *
+ * The content of a Film is held in a Playlist (created and managed by the Film).
*/
- class Film : public boost::enable_shared_from_this<Film>
+ class Film : public boost::enable_shared_from_this<Film>, public boost::noncopyable
{
public:
- Film (std::string d, bool must_exist = true);
- Film (Film const &);
- ~Film ();
+ Film (boost::filesystem::path);
- std::string j2k_dir () const;
+ std::string info_dir () const;
+ std::string j2c_path (int, Eyes, bool) const;
+ std::string info_path (int, Eyes) const;
+ std::string internal_video_mxf_dir () const;
+ std::string internal_video_mxf_filename () const;
+ boost::filesystem::path audio_analysis_path (boost::shared_ptr<const AudioContent>) const;
- void examine_content ();
- void send_dcp_to_tms ();
+ std::string video_mxf_filename () const;
+ std::string audio_mxf_filename () const;
- void make_dcp (bool);
+ void send_dcp_to_tms ();
+ void make_dcp ();
/** @return Logger.
* It is safe to call this from any thread.
*/
- Log* log () const {
+ boost::shared_ptr<Log> log () const {
return _log;
}
std::string file (std::string f) const;
std::string dir (std::string d) const;
- std::string content_path () const;
- ContentType content_type () const;
-
- int target_audio_sample_rate () const;
-
- void write_metadata () const;
void read_metadata ();
+ void write_metadata () const;
- Size cropped_size (Size) const;
- boost::optional<int> dcp_length () const;
- std::string dci_name () const;
- std::string dcp_name () const;
+ std::string dci_name (bool if_created_now) const;
+ std::string dcp_name (bool if_created_now = false) const;
/** @return true if our state has changed since we last saved it */
bool dirty () const {
return _dirty;
}
- int audio_channels () const;
+ libdcp::Size full_frame () const;
- void set_dci_date_today ();
+ bool have_dcp () const;
+
+ boost::shared_ptr<Player> make_player () const;
+ boost::shared_ptr<Playlist> playlist () const;
+
+ OutputAudioFrame audio_frame_rate () const;
+
+ OutputAudioFrame time_to_audio_frames (Time) const;
+ OutputVideoFrame time_to_video_frames (Time) const;
+ Time video_frames_to_time (OutputVideoFrame) const;
+ Time audio_frames_to_time (OutputAudioFrame) const;
+
+ /* Proxies for some Playlist methods */
+
+ ContentList content () const;
+
+ Time length () const;
+ bool has_subtitles () const;
+ OutputVideoFrame best_video_frame_rate () const;
+ void make_kdms (
+ std::list<boost::shared_ptr<Screen> >,
+ boost::posix_time::ptime from,
+ boost::posix_time::ptime until,
+ std::string directory
+ ) const;
+
/** Identifiers for the parts of our state;
used for signalling changes.
*/
NONE,
NAME,
USE_DCI_NAME,
+ /** The playlist's content list has changed (i.e. content has been added, moved around or removed) */
CONTENT,
- TRUST_CONTENT_HEADER,
DCP_CONTENT_TYPE,
- FORMAT,
- CROP,
- FILTERS,
+ CONTAINER,
+ RESOLUTION,
SCALER,
- DCP_TRIM_START,
- DCP_TRIM_END,
- REEL_SIZE,
- DCP_AB,
- CONTENT_AUDIO_STREAM,
- EXTERNAL_AUDIO,
- USE_CONTENT_AUDIO,
- AUDIO_GAIN,
- AUDIO_DELAY,
- STILL_DURATION,
- SUBTITLE_STREAM,
WITH_SUBTITLES,
- SUBTITLE_OFFSET,
- SUBTITLE_SCALE,
+ ENCRYPTED,
- COLOUR_LUT,
J2K_BANDWIDTH,
DCI_METADATA,
- SIZE,
- LENGTH,
- CONTENT_AUDIO_STREAMS,
- SUBTITLE_STREAMS,
- FRAMES_PER_SECOND,
+ VIDEO_FRAME_RATE,
+ AUDIO_CHANNELS,
+ /** The setting of _three_d has been changed */
+ THREE_D,
+ SEQUENCE_VIDEO,
+ INTEROP,
};
/* GET */
std::string directory () const {
- boost::mutex::scoped_lock lm (_directory_mutex);
return _directory;
}
std::string name () const {
- boost::mutex::scoped_lock lm (_state_mutex);
return _name;
}
bool use_dci_name () const {
- boost::mutex::scoped_lock lm (_state_mutex);
return _use_dci_name;
}
- std::string content () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _content;
- }
-
- bool trust_content_header () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _trust_content_header;
- }
-
DCPContentType const * dcp_content_type () const {
- boost::mutex::scoped_lock lm (_state_mutex);
return _dcp_content_type;
}
- Format const * format () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _format;
+ Ratio const * container () const {
+ return _container;
}
- Crop crop () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _crop;
- }
-
- std::vector<Filter const *> filters () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _filters;
+ Resolution resolution () const {
+ return _resolution;
}
Scaler const * scaler () const {
- boost::mutex::scoped_lock lm (_state_mutex);
return _scaler;
}
- SourceFrame dcp_trim_start () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _dcp_trim_start;
- }
-
- SourceFrame dcp_trim_end () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _dcp_trim_end;
- }
-
- boost::optional<uint64_t> reel_size () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _reel_size;
- }
-
- bool dcp_ab () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _dcp_ab;
- }
-
- boost::shared_ptr<AudioStream> content_audio_stream () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _content_audio_stream;
- }
-
- std::vector<std::string> external_audio () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _external_audio;
- }
-
- bool use_content_audio () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _use_content_audio;
- }
-
- float audio_gain () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _audio_gain;
- }
-
- int audio_delay () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _audio_delay;
- }
-
- int still_duration () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _still_duration;
- }
-
- boost::shared_ptr<SubtitleStream> subtitle_stream () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _subtitle_stream;
- }
-
bool with_subtitles () const {
- boost::mutex::scoped_lock lm (_state_mutex);
return _with_subtitles;
}
- int subtitle_offset () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _subtitle_offset;
- }
-
- float subtitle_scale () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _subtitle_scale;
- }
-
+ bool encrypted () const {
- boost::mutex::scoped_lock lm (_state_mutex);
+ return _encrypted;
+ }
+
- int colour_lut () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _colour_lut;
- }
-
int j2k_bandwidth () const {
- boost::mutex::scoped_lock lm (_state_mutex);
return _j2k_bandwidth;
}
- std::string audio_language () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _audio_language;
- }
-
- std::string subtitle_language () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _subtitle_language;
- }
-
- std::string territory () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _territory;
- }
-
- std::string rating () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _rating;
- }
-
- std::string studio () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _studio;
- }
-
- std::string facility () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _facility;
- }
-
- std::string package_type () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _package_type;
+ DCIMetadata dci_metadata () const {
+ return _dci_metadata;
}
- Size size () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _size;
+ /** @return The frame rate of the DCP */
+ int video_frame_rate () const {
+ return _video_frame_rate;
}
- boost::optional<SourceFrame> length () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _length;
+ int audio_channels () const {
+ return _audio_channels;
}
-
- std::string content_digest () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _content_digest;
+
+ bool three_d () const {
+ return _three_d;
}
-
- std::vector<boost::shared_ptr<AudioStream> > content_audio_streams () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _content_audio_streams;
+
+ bool sequence_video () const {
+ return _sequence_video;
}
- std::vector<boost::shared_ptr<SubtitleStream> > subtitle_streams () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _subtitle_streams;
+ bool interop () const {
+ return _interop;
}
- float frames_per_second () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- if (content_type() == STILL) {
- return 24;
- }
-
- return _frames_per_second;
- }
-
- boost::shared_ptr<AudioStream> audio_stream () const;
-
/* SET */
void set_directory (std::string);
void set_name (std::string);
void set_use_dci_name (bool);
- void set_content (std::string);
- void set_trust_content_header (bool);
+ void examine_and_add_content (boost::shared_ptr<Content>);
+ void add_content (boost::shared_ptr<Content>);
+ void remove_content (boost::shared_ptr<Content>);
void set_dcp_content_type (DCPContentType const *);
- void set_format (Format const *);
- void set_crop (Crop);
- void set_left_crop (int);
- void set_right_crop (int);
- void set_top_crop (int);
- void set_bottom_crop (int);
- void set_filters (std::vector<Filter const *>);
+ void set_container (Ratio const *);
+ void set_resolution (Resolution);
void set_scaler (Scaler const *);
- void set_dcp_trim_start (int);
- void set_dcp_trim_end (int);
- void set_reel_size (uint64_t);
- void unset_reel_size ();
- void set_dcp_ab (bool);
- void set_content_audio_stream (boost::shared_ptr<AudioStream>);
- void set_external_audio (std::vector<std::string>);
- void set_use_content_audio (bool);
- void set_audio_gain (float);
- void set_audio_delay (int);
- void set_still_duration (int);
- void set_subtitle_stream (boost::shared_ptr<SubtitleStream>);
void set_with_subtitles (bool);
- void set_subtitle_offset (int);
- void set_subtitle_scale (float);
+ void set_encrypted (bool);
- void set_colour_lut (int);
void set_j2k_bandwidth (int);
- void set_audio_language (std::string);
- void set_subtitle_language (std::string);
- void set_territory (std::string);
- void set_rating (std::string);
- void set_studio (std::string);
- void set_facility (std::string);
- void set_package_type (std::string);
- void set_size (Size);
- void set_length (SourceFrame);
- void unset_length ();
- void set_content_digest (std::string);
- void set_content_audio_streams (std::vector<boost::shared_ptr<AudioStream> >);
- void set_subtitle_streams (std::vector<boost::shared_ptr<SubtitleStream> >);
- void set_frames_per_second (float);
-
- /** Emitted when some property has changed */
+ void set_dci_metadata (DCIMetadata);
+ void set_video_frame_rate (int);
+ void set_audio_channels (int);
+ void set_three_d (bool);
+ void set_dci_date_today ();
+ void set_sequence_video (bool);
+ void set_interop (bool);
+
+ /** Emitted when some property has of the Film has changed */
mutable boost::signals2::signal<void (Property)> Changed;
+ /** Emitted when some property of our content has changed */
+ mutable boost::signals2::signal<void (boost::weak_ptr<Content>, int)> ContentChanged;
+
/** Current version number of the state file */
static int const state_version;
private:
-
- /** Log to write to */
- Log* _log;
-
- /** Any running ExamineContentJob, or 0 */
- boost::shared_ptr<ExamineContentJob> _examine_content_job;
-
- /** The date that we should use in a DCI name */
- boost::gregorian::date _dci_date;
void signal_changed (Property);
- void examine_content_finished ();
+ std::string video_identifier () const;
+ void playlist_changed ();
+ void playlist_content_changed (boost::weak_ptr<Content>, int);
+ std::string filename_safe_name () const;
+ void maybe_add_content (boost::weak_ptr<Job>, boost::weak_ptr<Content>);
+
+ /** Log to write to */
+ boost::shared_ptr<Log> _log;
+ boost::shared_ptr<Playlist> _playlist;
/** Complete path to directory containing the film metadata;
* must not be relative.
*/
std::string _directory;
- /** Mutex for _directory */
- mutable boost::mutex _directory_mutex;
- /** Name for DVD-o-matic */
+ /** Name for DCP-o-matic */
std::string _name;
/** True if a auto-generated DCI-compliant name should be used for our DCP */
bool _use_dci_name;
- /** File or directory containing content; may be relative to our directory
- * or an absolute path.
- */
- std::string _content;
- /** If this is true, we will believe the length specified by the content
- * file's header; if false, we will run through the whole content file
- * the first time we see it in order to obtain the length.
- */
- bool _trust_content_header;
/** The type of content that this Film represents (feature, trailer etc.) */
DCPContentType const * _dcp_content_type;
- /** The format to present this Film in (flat, scope, etc.) */
- Format const * _format;
- /** The crop to apply to the source */
- Crop _crop;
- /** Video filters that should be used when generating DCPs */
- std::vector<Filter const *> _filters;
+ /** The container to put this Film in (flat, scope, etc.) */
+ Ratio const * _container;
+ /** DCP resolution (2K or 4K) */
+ Resolution _resolution;
/** Scaler algorithm to use */
Scaler const * _scaler;
- /** Frames to trim off the start of the DCP */
- int _dcp_trim_start;
- /** Frames to trim off the end of the DCP */
- int _dcp_trim_end;
- /** Approximate target reel size in bytes; if not set, use a single reel */
- boost::optional<uint64_t> _reel_size;
- /** true to create an A/B comparison DCP, where the left half of the image
- is the video without any filters or post-processing, and the right half
- has the specified filters and post-processing.
- */
- bool _dcp_ab;
- /** The audio stream to use from our content */
- boost::shared_ptr<AudioStream> _content_audio_stream;
- /** List of filenames of external audio files, in channel order
- (L, R, C, Lfe, Ls, Rs)
- */
- std::vector<std::string> _external_audio;
- /** true to use audio from our content file; false to use external audio */
- bool _use_content_audio;
- /** Gain to apply to audio in dB */
- float _audio_gain;
- /** Delay to apply to audio (positive moves audio later) in milliseconds */
- int _audio_delay;
- /** Duration to make still-sourced films (in seconds) */
- int _still_duration;
- boost::shared_ptr<SubtitleStream> _subtitle_stream;
/** True if subtitles should be shown for this film */
bool _with_subtitles;
- /** y offset for placing subtitles, in source pixels; +ve is further down
- the frame, -ve is further up.
- */
- int _subtitle_offset;
- /** scale factor to apply to subtitles */
- float _subtitle_scale;
+ bool _encrypted;
-
- /** index of colour LUT to use when converting RGB to XYZ.
- * 0: sRGB
- * 1: Rec 709
- */
- int _colour_lut;
/** bandwidth for J2K files in bits per second */
int _j2k_bandwidth;
-
- /* DCI naming stuff */
- std::string _audio_language;
- std::string _subtitle_language;
- std::string _territory;
- std::string _rating;
- std::string _studio;
- std::string _facility;
- std::string _package_type;
-
- /* Data which are cached to speed things up */
-
- /** Size, in pixels, of the source (ignoring cropping) */
- Size _size;
- /** The length of the source, in video frames (as far as we know) */
- boost::optional<SourceFrame> _length;
- /** MD5 digest of our content file */
- std::string _content_digest;
- /** The audio streams in our content */
- std::vector<boost::shared_ptr<AudioStream> > _content_audio_streams;
- /** A stream to represent possible external audio (will always exist) */
- boost::shared_ptr<AudioStream> _external_audio_stream;
- /** the subtitle streams that we can use */
- std::vector<boost::shared_ptr<SubtitleStream> > _subtitle_streams;
- /** Frames per second of the source */
- float _frames_per_second;
+ /** DCI naming stuff */
+ DCIMetadata _dci_metadata;
+ /** Frames per second to run our DCP at */
+ int _video_frame_rate;
+ /** The date that we should use in a DCI name */
+ boost::gregorian::date _dci_date;
+ /** Number of audio channels to put in the DCP */
+ int _audio_channels;
+ /** If true, the DCP will be written in 3D mode; otherwise in 2D.
+ This will be regardless of what content is on the playlist.
+ */
+ bool _three_d;
+ bool _sequence_video;
+ bool _interop;
/** true if our state has changed since we last saved it */
mutable bool _dirty;
- /** Mutex for all state except _directory */
- mutable boost::mutex _state_mutex;
-
friend class paths_test;
+ friend class film_metadata_test;
};
#endif
#include <iomanip>
#include <iostream>
#include <fstream>
- #ifdef DVDOMATIC_POSIX
+ #include <climits>
+ #ifdef DCPOMATIC_POSIX
#include <execinfo.h>
#include <cxxabi.h>
#endif
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <boost/filesystem.hpp>
+ #include <glib.h>
#include <openjpeg.h>
#include <openssl/md5.h>
#include <magick/MagickCore.h>
#include <magick/version.h>
#include <libdcp/version.h>
+#include <libdcp/util.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include "util.h"
#include "exceptions.h"
#include "scaler.h"
- #include "format.h"
#include "dcp_content_type.h"
#include "filter.h"
#include "sound_processor.h"
+ #include "config.h"
+ #include "ratio.h"
+ #include "job.h"
+ #ifdef DCPOMATIC_WINDOWS
+ #include "stack.hpp"
+ #endif
- using namespace std;
- using namespace boost;
-
- thread::id ui_thread;
+ #include "i18n.h"
+
+ using std::string;
+ using std::stringstream;
+ using std::setfill;
+ using std::ostream;
+ using std::endl;
+ using std::vector;
+ using std::hex;
+ using std::setw;
+ using std::ifstream;
+ using std::ios;
+ using std::min;
+ using std::max;
+ using std::list;
+ using std::multimap;
+ using std::istream;
+ using std::numeric_limits;
+ using std::pair;
+ using std::ofstream;
+ using boost::shared_ptr;
+ using boost::thread;
+ using boost::lexical_cast;
+ using boost::optional;
+ using libdcp::Size;
+
+ static boost::thread::id ui_thread;
+ static boost::filesystem::path backtrace_file;
/** Convert some number of seconds to a string representation
* in hours, minutes and seconds.
m -= (h * 60);
stringstream hms;
- hms << h << ":";
+ hms << h << N_(":");
hms.width (2);
- hms << setfill ('0') << m << ":";
+ hms << std::setfill ('0') << m << N_(":");
hms.width (2);
- hms << setfill ('0') << s;
+ hms << std::setfill ('0') << s;
return hms.str ();
}
if (h > 0) {
if (m > 30) {
- ap << (h + 1) << " hours";
+ ap << (h + 1) << N_(" ") << _("hours");
} else {
if (h == 1) {
- ap << "1 hour";
+ ap << N_("1 ") << _("hour");
} else {
- ap << h << " hours";
+ ap << h << N_(" ") << _("hours");
}
}
} else if (m > 0) {
if (m == 1) {
- ap << "1 minute";
+ ap << N_("1 ") << _("minute");
} else {
- ap << m << " minutes";
+ ap << m << N_(" ") << _("minutes");
}
} else {
- ap << s << " seconds";
+ ap << s << N_(" ") << _("seconds");
}
return ap.str ();
}
- #ifdef DVDOMATIC_POSIX
+ #ifdef DCPOMATIC_POSIX
/** @param l Mangled C++ identifier.
* @return Demangled version.
*/
static string
demangle (string l)
{
- string::size_type const b = l.find_first_of ("(");
+ string::size_type const b = l.find_first_of (N_("("));
if (b == string::npos) {
return l;
}
- string::size_type const p = l.find_last_of ("+");
+ string::size_type const p = l.find_last_of (N_("+"));
if (p == string::npos) {
return l;
}
stacktrace (ostream& out, int levels)
{
void *array[200];
- size_t size;
- char **strings;
- size_t i;
-
- size = backtrace (array, 200);
- strings = backtrace_symbols (array, size);
+ size_t size = backtrace (array, 200);
+ char** strings = backtrace_symbols (array, size);
if (strings) {
- for (i = 0; i < size && (levels == 0 || i < size_t(levels)); i++) {
- out << " " << demangle (strings[i]) << endl;
+ for (size_t i = 0; i < size && (levels == 0 || i < size_t(levels)); i++) {
+ out << N_(" ") << demangle (strings[i]) << "\n";
}
free (strings);
ffmpeg_version_to_string (int v)
{
stringstream s;
- s << ((v & 0xff0000) >> 16) << "." << ((v & 0xff00) >> 8) << "." << (v & 0xff);
+ s << ((v & 0xff0000) >> 16) << N_(".") << ((v & 0xff00) >> 8) << N_(".") << (v & 0xff);
return s.str ();
}
dependency_version_summary ()
{
stringstream s;
- s << "libopenjpeg " << opj_version () << ", "
- << "libavcodec " << ffmpeg_version_to_string (avcodec_version()) << ", "
- << "libavfilter " << ffmpeg_version_to_string (avfilter_version()) << ", "
- << "libavformat " << ffmpeg_version_to_string (avformat_version()) << ", "
- << "libavutil " << ffmpeg_version_to_string (avutil_version()) << ", "
- << "libpostproc " << ffmpeg_version_to_string (postproc_version()) << ", "
- << "libswscale " << ffmpeg_version_to_string (swscale_version()) << ", "
- << MagickVersion << ", "
- << "libssh " << ssh_version (0) << ", "
- << "libdcp " << libdcp::version << " git " << libdcp::git_commit;
+ 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_("libpostproc ") << ffmpeg_version_to_string (postproc_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 ();
}
return t.tv_sec + (double (t.tv_usec) / 1e6);
}
- /** Call the required functions to set up DVD-o-matic's static arrays, etc.
+ #ifdef DCPOMATIC_WINDOWS
+ LONG WINAPI exception_handler(struct _EXCEPTION_POINTERS *)
+ {
+ dbg::stack s;
+ ofstream f (backtrace_file.string().c_str());
+ std::copy(s.begin(), s.end(), std::ostream_iterator<dbg::stack_frame>(f, "\n"));
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ #endif
+
+ /** Call the required functions to set up DCP-o-matic's static arrays, etc.
* Must be called from the UI thread, if there is one.
*/
void
- dvdomatic_setup ()
+ dcpomatic_setup ()
{
- libdcp::init ();
+ #ifdef DCPOMATIC_WINDOWS
+ backtrace_file /= g_get_user_config_dir ();
+ backtrace_file /= "backtrace.txt";
+ SetUnhandledExceptionFilter(exception_handler);
+ #endif
+
+ avfilter_register_all ();
- Format::setup_formats ();
+ Ratio::setup_ratios ();
DCPContentType::setup_dcp_content_types ();
Scaler::setup_scalers ();
Filter::setup_filters ();
SoundProcessor::setup_sound_processors ();
- ui_thread = this_thread::get_id ();
+ ui_thread = boost::this_thread::get_id ();
}
- /** @param start Start position for the crop within the image.
- * @param size Size of the cropped area.
- * @return FFmpeg crop filter string.
- */
- string
- crop_string (Position start, Size size)
+ #ifdef DCPOMATIC_WINDOWS
+ boost::filesystem::path
+ mo_path ()
{
- stringstream s;
- s << "crop=" << size.width << ":" << size.height << ":" << start.x << ":" << start.y;
- return s.str ();
+ wchar_t buffer[512];
+ GetModuleFileName (0, buffer, 512 * sizeof(wchar_t));
+ boost::filesystem::path p (buffer);
+ p = p.parent_path ();
+ p = p.parent_path ();
+ p /= "locale";
+ return p;
+ }
+ #endif
+
+ void
+ dcpomatic_setup_gettext_i18n (string lang)
+ {
+ #ifdef DCPOMATIC_POSIX
+ lang += ".UTF8";
+ #endif
+
+ if (!lang.empty ()) {
+ /* Override our environment language; this is essential on
+ Windows.
+ */
+ char cmd[64];
+ snprintf (cmd, sizeof(cmd), "LANGUAGE=%s", lang.c_str ());
+ putenv (cmd);
+ snprintf (cmd, sizeof(cmd), "LANG=%s", lang.c_str ());
+ putenv (cmd);
+ }
+
+ setlocale (LC_ALL, "");
+ textdomain ("libdcpomatic");
+
+ #ifdef DCPOMATIC_WINDOWS
+ bindtextdomain ("libdcpomatic", mo_path().string().c_str());
+ bind_textdomain_codeset ("libdcpomatic", "UTF8");
+ #endif
+
+ #ifdef DCPOMATIC_POSIX
+ bindtextdomain ("libdcpomatic", POSIX_LOCALE_PREFIX);
+ #endif
}
/** @param s A string.
for (string::size_type i = 0; i < s.length(); ++i) {
if (s[i] == ' ' && !in_quotes) {
out.push_back (c);
- c = "";
+ c = N_("");
} else if (s[i] == '"') {
in_quotes = !in_quotes;
} else {
stringstream s;
for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
- s << hex << setfill('0') << setw(2) << ((int) digest[i]);
+ s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
}
return s.str ();
* @return MD5 digest of file's contents.
*/
string
- md5_digest (string file)
+ md5_digest (boost::filesystem::path file)
{
- ifstream f (file.c_str(), ios::binary);
+ ifstream f (file.string().c_str(), std::ios::binary);
if (!f.good ()) {
- throw OpenFileError (file);
+ throw OpenFileError (file.string());
}
- f.seekg (0, ios::end);
+ f.seekg (0, std::ios::end);
int bytes = f.tellg ();
- f.seekg (0, ios::beg);
+ f.seekg (0, std::ios::beg);
int const buffer_size = 64 * 1024;
char buffer[buffer_size];
stringstream s;
for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
- s << hex << setfill('0') << setw(2) << ((int) digest[i]);
+ s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
}
return s.str ();
}
- /** @param fps Arbitrary frames-per-second value.
- * @return DCPFrameRate for this frames-per-second.
- */
- DCPFrameRate
- dcp_frame_rate (float fps)
+ /** @param job Optional job for which to report progress */
+ string
+ md5_digest_directory (boost::filesystem::path directory, shared_ptr<Job> job)
{
- DCPFrameRate dfr;
+ int const buffer_size = 64 * 1024;
+ char buffer[buffer_size];
- dfr.run_fast = (fps != rint (fps));
- dfr.frames_per_second = rint (fps);
- dfr.skip = 1;
+ MD5_CTX md5_context;
+ MD5_Init (&md5_context);
- /* XXX: somewhat arbitrary */
- if (fps == 50) {
- dfr.frames_per_second = 25;
- dfr.skip = 2;
+ int files = 0;
+ if (job) {
+ for (boost::filesystem::directory_iterator i(directory); i != boost::filesystem::directory_iterator(); ++i) {
+ ++files;
+ }
}
- return dfr;
- }
+ int j = 0;
+ for (boost::filesystem::directory_iterator i(directory); i != boost::filesystem::directory_iterator(); ++i) {
+ ifstream f (i->path().string().c_str(), std::ios::binary);
+ if (!f.good ()) {
+ throw OpenFileError (i->path().string());
+ }
+
+ f.seekg (0, std::ios::end);
+ int bytes = f.tellg ();
+ f.seekg (0, std::ios::beg);
+
+ while (bytes > 0) {
+ int const t = min (bytes, buffer_size);
+ f.read (buffer, t);
+ MD5_Update (&md5_context, buffer, t);
+ bytes -= t;
+ }
- /** @param An arbitrary sampling rate.
- * @return The appropriate DCP-approved sampling rate (48kHz or 96kHz).
- */
- int
- dcp_audio_sample_rate (int fs)
- {
- if (fs <= 48000) {
- return 48000;
+ if (job) {
+ job->set_progress (float (j) / files);
+ ++j;
+ }
}
- return 96000;
- }
+ unsigned char digest[MD5_DIGEST_LENGTH];
+ MD5_Final (digest, &md5_context);
- int
- dcp_audio_channels (int f)
- {
- if (f == 1) {
- /* The source is mono, so to put the mono channel into
- the centre we need to generate a 5.1 soundtrack.
- */
- return 6;
+ stringstream s;
+ for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
+ s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
}
- return f;
+ return s.str ();
}
-
- bool operator== (Size const & a, Size const & b)
+ static bool
+ about_equal (float a, float b)
{
- return (a.width == b.width && a.height == b.height);
- }
+ /* A film of F seconds at f FPS will be Ff frames;
+ Consider some delta FPS d, so if we run the same
+ film at (f + d) FPS it will last F(f + d) seconds.
- bool operator!= (Size const & a, Size const & b)
- {
- return !(a == b);
- }
+ Hence the difference in length over the length of the film will
+ be F(f + d) - Ff frames
+ = Ff + Fd - Ff frames
+ = Fd frames
+ = Fd/f seconds
+
+ So if we accept a difference of 1 frame, ie 1/f seconds, we can
+ say that
- bool operator== (Crop const & a, Crop const & b)
- {
- return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom);
- }
+ 1/f = Fd/f
+ ie 1 = Fd
+ ie d = 1/F
+
+ So for a 3hr film, ie F = 3 * 60 * 60 = 10800, the acceptable
+ FPS error is 1/F ~= 0.0001 ~= 10-e4
+ */
- bool operator!= (Crop const & a, Crop const & b)
- {
- return !(a == b);
+ return (fabs (a - b) < 1e-4);
}
- /** @param index Colour LUT index.
- * @return Human-readable name.
+ /** @param An arbitrary audio frame rate.
+ * @return The appropriate DCP-approved frame rate (48kHz or 96kHz).
*/
- string
- colour_lut_index_to_name (int index)
+ int
+ dcp_audio_frame_rate (int fs)
{
- switch (index) {
- case 0:
- return "sRGB";
- case 1:
- return "Rec 709";
+ if (fs <= 48000) {
+ return 48000;
}
- assert (false);
- return "";
+ return 96000;
}
- Socket::Socket ()
+ Socket::Socket (int timeout)
: _deadline (_io_service)
, _socket (_io_service)
- , _buffer_data (0)
+ , _timeout (timeout)
{
- _deadline.expires_at (posix_time::pos_infin);
+ _deadline.expires_at (boost::posix_time::pos_infin);
check ();
}
void
Socket::check ()
{
- if (_deadline.expires_at() <= asio::deadline_timer::traits_type::now ()) {
+ if (_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now ()) {
_socket.close ();
- _deadline.expires_at (posix_time::pos_infin);
+ _deadline.expires_at (boost::posix_time::pos_infin);
}
_deadline.async_wait (boost::bind (&Socket::check, this));
}
- /** Blocking connect with timeout.
+ /** Blocking connect.
* @param endpoint End-point to connect to.
- * @param timeout Time-out in seconds.
*/
void
- Socket::connect (asio::ip::basic_resolver_entry<asio::ip::tcp> const & endpoint, int timeout)
+ Socket::connect (boost::asio::ip::basic_resolver_entry<boost::asio::ip::tcp> const & endpoint)
{
- _deadline.expires_from_now (posix_time::seconds (timeout));
- system::error_code ec = asio::error::would_block;
- _socket.async_connect (endpoint, lambda::var(ec) = lambda::_1);
+ _deadline.expires_from_now (boost::posix_time::seconds (_timeout));
+ boost::system::error_code ec = boost::asio::error::would_block;
+ _socket.async_connect (endpoint, boost::lambda::var(ec) = boost::lambda::_1);
do {
_io_service.run_one();
- } while (ec == asio::error::would_block);
+ } while (ec == boost::asio::error::would_block);
if (ec || !_socket.is_open ()) {
- throw NetworkError ("connect timed out");
+ throw NetworkError (_("connect timed out"));
}
}
- /** Blocking write with timeout.
+ /** Blocking write.
* @param data Buffer to write.
* @param size Number of bytes to write.
- * @param timeout Time-out, in seconds.
*/
void
- Socket::write (uint8_t const * data, int size, int timeout)
+ Socket::write (uint8_t const * data, int size)
{
- _deadline.expires_from_now (posix_time::seconds (timeout));
- system::error_code ec = asio::error::would_block;
+ _deadline.expires_from_now (boost::posix_time::seconds (_timeout));
+ boost::system::error_code ec = boost::asio::error::would_block;
- asio::async_write (_socket, asio::buffer (data, size), lambda::var(ec) = lambda::_1);
+ boost::asio::async_write (_socket, boost::asio::buffer (data, size), boost::lambda::var(ec) = boost::lambda::_1);
+
do {
_io_service.run_one ();
- } while (ec == asio::error::would_block);
-
- if (ec) {
- throw NetworkError ("write timed out");
- }
- }
-
- /** Blocking read with timeout.
- * @param data Buffer to read to.
- * @param size Number of bytes to read.
- * @param timeout Time-out, in seconds.
- */
- int
- Socket::read (uint8_t* data, int size, int timeout)
- {
- _deadline.expires_from_now (posix_time::seconds (timeout));
- system::error_code ec = asio::error::would_block;
+ } while (ec == boost::asio::error::would_block);
- int amount_read = 0;
-
- _socket.async_read_some (
- asio::buffer (data, size),
- (lambda::var(ec) = lambda::_1, lambda::var(amount_read) = lambda::_2)
- );
-
- do {
- _io_service.run_one ();
- } while (ec == asio::error::would_block);
-
if (ec) {
- amount_read = 0;
+ throw NetworkError (ec.message ());
}
-
- return amount_read;
}
- /** Mark some data as being `consumed', so that it will not be returned
- * as data again.
- * @param size Amount of data to consume, in bytes.
- */
void
- Socket::consume (int size)
+ Socket::write (uint32_t v)
{
- assert (_buffer_data >= size);
-
- _buffer_data -= size;
- if (_buffer_data > 0) {
- /* Shift still-valid data to the start of the buffer */
- memmove (_buffer, _buffer + size, _buffer_data);
- }
+ v = htonl (v);
+ write (reinterpret_cast<uint8_t*> (&v), 4);
}
- /** Read a definite amount of data from our socket, and mark
- * it as consumed.
- * @param data Where to put the data.
+ /** Blocking read.
+ * @param data Buffer to read to.
* @param size Number of bytes to read.
*/
void
- Socket::read_definite_and_consume (uint8_t* data, int size, int timeout)
- {
- int const from_buffer = min (_buffer_data, size);
- if (from_buffer > 0) {
- /* Get data from our buffer */
- memcpy (data, _buffer, from_buffer);
- consume (from_buffer);
- /* Update our output state */
- data += from_buffer;
- size -= from_buffer;
- }
-
- /* read() the rest */
- while (size > 0) {
- int const n = read (data, size, timeout);
- if (n <= 0) {
- throw NetworkError ("could not read");
- }
-
- data += n;
- size -= n;
- }
- }
-
- /** Read as much data as is available, up to some limit.
- * @param data Where to put the data.
- * @param size Maximum amount of data to read.
- */
- void
- Socket::read_indefinite (uint8_t* data, int size, int timeout)
+ Socket::read (uint8_t* data, int size)
{
- assert (size < int (sizeof (_buffer)));
+ _deadline.expires_from_now (boost::posix_time::seconds (_timeout));
+ boost::system::error_code ec = boost::asio::error::would_block;
- /* Amount of extra data we need to read () */
- int to_read = size - _buffer_data;
- while (to_read > 0) {
- /* read as much of it as we can (into our buffer) */
- int const n = read (_buffer + _buffer_data, to_read, timeout);
- if (n <= 0) {
- throw NetworkError ("could not read");
- }
+ boost::asio::async_read (_socket, boost::asio::buffer (data, size), boost::lambda::var(ec) = boost::lambda::_1);
- to_read -= n;
- _buffer_data += n;
+ do {
+ _io_service.run_one ();
+ } while (ec == boost::asio::error::would_block);
+
+ if (ec) {
+ throw NetworkError (ec.message ());
}
-
- assert (_buffer_data >= size);
-
- /* copy data into the output buffer */
- assert (size >= _buffer_data);
- memcpy (data, _buffer, size);
}
- /** @param other A Rect.
- * @return The intersection of this with `other'.
- */
- Rect
- Rect::intersection (Rect const & other) const
+ uint32_t
+ Socket::read_uint32 ()
{
- int const tx = max (x, other.x);
- int const ty = max (y, other.y);
-
- return Rect (
- tx, ty,
- min (x + width, other.x + other.width) - tx,
- min (y + height, other.y + other.height) - ty
- );
+ uint32_t v;
+ read (reinterpret_cast<uint8_t *> (&v), 4);
+ return ntohl (v);
}
/** Round a number up to the nearest multiple of another number.
return a - (a % t);
}
- int
- stride_lookup (int c, int const * stride)
- {
- return stride[c];
- }
-
/** 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
get_required_string (multimap<string, string> const & kv, string k)
{
if (kv.count (k) > 1) {
- throw StringError ("unexpected multiple keys in key-value set");
+ throw StringError (N_("unexpected multiple keys in key-value set"));
}
multimap<string, string>::const_iterator i = kv.find (k);
if (i == kv.end ()) {
- throw StringError (String::compose ("missing key %1 in key-value set", k));
+ throw StringError (String::compose (_("missing key %1 in key-value set"), k));
}
return i->second;
get_optional_string (multimap<string, string> const & kv, string k)
{
if (kv.count (k) > 1) {
- throw StringError ("unexpected multiple keys in key-value set");
+ throw StringError (N_("unexpected multiple keys in key-value set"));
}
multimap<string, string>::const_iterator i = kv.find (k);
if (i == kv.end ()) {
- return "";
+ return N_("");
}
return i->second;
get_optional_int (multimap<string, string> const & kv, string k)
{
if (kv.count (k) > 1) {
- throw StringError ("unexpected multiple keys in key-value set");
+ throw StringError (N_("unexpected multiple keys in key-value set"));
}
multimap<string, string>::const_iterator i = kv.find (k);
return lexical_cast<int> (i->second);
}
- /** Construct an AudioBuffers. Audio data is undefined after this constructor.
- * @param channels Number of channels.
- * @param frames Number of frames to reserve space for.
- */
- AudioBuffers::AudioBuffers (int channels, int frames)
- : _channels (channels)
- , _frames (frames)
- , _allocated_frames (frames)
- {
- _data = new float*[_channels];
- for (int i = 0; i < _channels; ++i) {
- _data[i] = new float[frames];
- }
- }
-
- /** Copy constructor.
- * @param other Other AudioBuffers; data is copied.
- */
- AudioBuffers::AudioBuffers (AudioBuffers const & other)
- : _channels (other._channels)
- , _frames (other._frames)
- , _allocated_frames (other._frames)
- {
- _data = new float*[_channels];
- for (int i = 0; i < _channels; ++i) {
- _data[i] = new float[_frames];
- memcpy (_data[i], other._data[i], _frames * sizeof (float));
- }
- }
-
- /** AudioBuffers destructor */
- AudioBuffers::~AudioBuffers ()
+ /** Trip an assert if the caller is not in the UI thread */
+ void
+ ensure_ui_thread ()
{
- for (int i = 0; i < _channels; ++i) {
- delete[] _data[i];
- }
-
- delete[] _data;
+ assert (boost::this_thread::get_id() == ui_thread);
}
- /** @param c Channel index.
- * @return Buffer for this channel.
+ /** @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'.
*/
- float*
- AudioBuffers::data (int c) const
+ int64_t
+ video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second)
{
- assert (c >= 0 && c < _channels);
- return _data[c];
+ return ((int64_t) v * audio_sample_rate / frames_per_second);
}
- /** Set the number of frames that these AudioBuffers will report themselves
- * as having.
- * @param f Frames; must be less than or equal to the number of allocated frames.
- */
- void
- AudioBuffers::set_frames (int f)
+ string
+ audio_channel_name (int c)
{
- assert (f <= _allocated_frames);
- _frames = f;
- }
+ assert (MAX_AUDIO_CHANNELS == 6);
- /** Make all samples on all channels silent */
- void
- AudioBuffers::make_silent ()
- {
- for (int i = 0; i < _channels; ++i) {
- make_silent (i);
- }
+ /* TRANSLATORS: these are the names of audio channels; Lfe (sub) is the low-frequency
+ enhancement channel (sub-woofer)./
+ */
+ string const channels[] = {
+ _("Left"),
+ _("Right"),
+ _("Centre"),
+ _("Lfe (sub)"),
+ _("Left surround"),
+ _("Right surround"),
+ };
+
+ return channels[c];
}
- /** Make all samples on a given channel silent.
- * @param c Channel.
- */
- void
- AudioBuffers::make_silent (int c)
+ FrameRateConversion::FrameRateConversion (float source, int dcp)
+ : skip (false)
+ , repeat (false)
+ , change_speed (false)
{
- assert (c >= 0 && c < _channels);
-
- for (int i = 0; i < _frames; ++i) {
- _data[c][i] = 0;
+ if (fabs (source / 2.0 - dcp) < (fabs (source - dcp))) {
+ skip = true;
+ } else if (fabs (source * 2 - dcp) < fabs (source - dcp)) {
+ repeat = true;
}
- }
- /** Copy data from another AudioBuffers to this one. All channels are copied.
- * @param from AudioBuffers to copy from; must have the same number of channels as this.
- * @param frames_to_copy Number of frames to copy.
- * @param read_offset Offset to read from in `from'.
- * @param write_offset Offset to write to in `to'.
- */
- void
- AudioBuffers::copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset)
- {
- assert (from->channels() == channels());
+ change_speed = !about_equal (source * factor(), dcp);
- assert (from);
- assert (read_offset >= 0 && (read_offset + frames_to_copy) <= from->_allocated_frames);
- assert (write_offset >= 0 && (write_offset + frames_to_copy) <= _allocated_frames);
+ if (!skip && !repeat && !change_speed) {
+ description = _("Content and DCP have the same rate.\n");
+ } else {
+ if (skip) {
+ description = _("DCP will use every other frame of the content.\n");
+ } else if (repeat) {
+ description = _("Each content frame will be doubled in the DCP.\n");
+ }
- for (int i = 0; i < _channels; ++i) {
- memcpy (_data[i] + write_offset, from->_data[i] + read_offset, frames_to_copy * sizeof(float));
+ if (change_speed) {
+ float const pc = dcp * 100 / (source * factor());
+ description += String::compose (_("DCP will run at %1%% of the content speed.\n"), pc);
+ }
}
}
- /** Move audio data around.
- * @param from Offset to move from.
- * @param to Offset to move to.
- * @param frames Number of frames to move.
- */
-
- void
- AudioBuffers::move (int from, int to, int frames)
+ LocaleGuard::LocaleGuard ()
+ : _old (0)
{
- if (frames == 0) {
- return;
- }
-
- assert (from >= 0);
- assert (from < _frames);
- assert (to >= 0);
- assert (to < _frames);
- assert (frames > 0);
- assert (frames <= _frames);
- assert ((from + frames) <= _frames);
- assert ((to + frames) <= _frames);
-
- for (int i = 0; i < _channels; ++i) {
- memmove (_data[i] + to, _data[i] + from, frames * sizeof(float));
- }
- }
+ char const * old = setlocale (LC_NUMERIC, 0);
- /** Trip an assert if the caller is not in the UI thread */
- void
- ensure_ui_thread ()
- {
- assert (this_thread::get_id() == ui_thread);
+ if (old) {
+ _old = strdup (old);
+ if (strcmp (_old, "C")) {
+ setlocale (LC_NUMERIC, "C");
+ }
+ }
}
- /** @param v Source 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 (SourceFrame v, float audio_sample_rate, float frames_per_second)
+ LocaleGuard::~LocaleGuard ()
{
- return ((int64_t) v * audio_sample_rate / frames_per_second);
+ setlocale (LC_NUMERIC, _old);
+ free (_old);
}
- /** @param f Filename.
- * @return true if this file is a still image, false if it is something else.
- */
bool
- still_image_file (string f)
+ valid_image_file (boost::filesystem::path f)
{
- #if BOOST_FILESYSTEM_VERSION == 3
- string ext = boost::filesystem::path(f).extension().string();
- #else
- string ext = boost::filesystem::path(f).extension();
- #endif
-
+ string ext = f.extension().string();
transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
-
- return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png");
+ return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp" || ext == ".tga");
}
- /** @return A pair containing CPU model name and the number of processors */
- pair<string, int>
- cpu_info ()
- {
- pair<string, int> info;
- info.second = 0;
-
- #ifdef DVDOMATIC_POSIX
- ifstream f ("/proc/cpuinfo");
- while (f.good ()) {
- string l;
- getline (f, l);
- if (boost::algorithm::starts_with (l, "model name")) {
- string::size_type const c = l.find (':');
- if (c != string::npos) {
- info.first = l.substr (c + 2);
- }
- } else if (boost::algorithm::starts_with (l, "processor")) {
- ++info.second;
- }
- }
- #endif
-
- return info;
- }
--- /dev/null
- bool const have_dcp = film && film->have_dcp();
+ /*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+ #include <iostream>
+ #include <fstream>
+ #include <boost/filesystem.hpp>
+ #ifdef __WXMSW__
+ #include <shellapi.h>
+ #endif
+ #ifdef __WXOSX__
+ #include <ApplicationServices/ApplicationServices.h>
+ #endif
+ #include <wx/generic/aboutdlgg.h>
+ #include <wx/stdpaths.h>
+ #include <wx/cmdline.h>
+ #include "wx/film_viewer.h"
+ #include "wx/film_editor.h"
+ #include "wx/job_manager_view.h"
+ #include "wx/config_dialog.h"
+ #include "wx/job_wrapper.h"
+ #include "wx/wx_util.h"
+ #include "wx/new_film_dialog.h"
+ #include "wx/properties_dialog.h"
+ #include "wx/wx_ui_signaller.h"
+ #include "wx/about_dialog.h"
++#include "wx/kdm_dialog.h"
+ #include "lib/film.h"
+ #include "lib/config.h"
+ #include "lib/util.h"
+ #include "lib/version.h"
+ #include "lib/ui_signaller.h"
+ #include "lib/log.h"
+ #include "lib/job_manager.h"
+ #include "lib/transcode_job.h"
++#include "lib/exceptions.h"
+
+ using std::cout;
+ using std::string;
+ using std::wstring;
+ using std::stringstream;
+ using std::map;
+ using std::make_pair;
+ using std::list;
+ using std::exception;
+ using std::ofstream;
+ using boost::shared_ptr;
+ using boost::dynamic_pointer_cast;
+
+ static FilmEditor* film_editor = 0;
+ static FilmViewer* film_viewer = 0;
+ static shared_ptr<Film> film;
+ static std::string log_level;
+ static std::string film_to_load;
+ static std::string film_to_create;
+ static wxMenu* jobs_menu = 0;
+
+ static void set_menu_sensitivity ();
+
+ class FilmChangedDialog
+ {
+ public:
+ FilmChangedDialog ()
+ {
+ _dialog = new wxMessageDialog (
+ 0,
+ wxString::Format (_("Save changes to film \"%s\" before closing?"), std_to_wx (film->name ()).data()),
+ _("Film changed"),
+ wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION
+ );
+ }
+
+ ~FilmChangedDialog ()
+ {
+ _dialog->Destroy ();
+ }
+
+ int run ()
+ {
+ return _dialog->ShowModal ();
+ }
+
+ private:
+ /* Not defined */
+ FilmChangedDialog (FilmChangedDialog const &);
+
+ wxMessageDialog* _dialog;
+ };
+
+
+ void
+ maybe_save_then_delete_film ()
+ {
+ if (!film) {
+ return;
+ }
+
+ if (film->dirty ()) {
+ FilmChangedDialog d;
+ switch (d.run ()) {
+ case wxID_NO:
+ break;
+ case wxID_YES:
+ film->write_metadata ();
+ break;
+ }
+ }
+
+ film.reset ();
+ }
+
+ #define ALWAYS 0x0
+ #define NEEDS_FILM 0x1
+ #define NOT_DURING_DCP_CREATION 0x2
+
+ map<wxMenuItem*, int> menu_items;
+
+ void
+ add_item (wxMenu* menu, wxString text, int id, int sens)
+ {
+ wxMenuItem* item = menu->Append (id, text);
+ menu_items.insert (make_pair (item, sens));
+ }
+
+ void
+ set_menu_sensitivity ()
+ {
+ list<shared_ptr<Job> > jobs = JobManager::instance()->get ();
+ list<shared_ptr<Job> >::iterator i = jobs.begin();
+ while (i != jobs.end() && dynamic_pointer_cast<TranscodeJob> (*i) == 0) {
+ ++i;
+ }
+ bool const dcp_creation = (i != jobs.end ());
+
+ for (map<wxMenuItem*, int>::iterator j = menu_items.begin(); j != menu_items.end(); ++j) {
+
+ bool enabled = true;
+
+ if ((j->second & NEEDS_FILM) && film == 0) {
+ enabled = false;
+ }
+
+ if ((j->second & NOT_DURING_DCP_CREATION) && dcp_creation) {
+ enabled = false;
+ }
+
+ j->first->Enable (enabled);
+ }
+ }
+
+ enum {
+ ID_file_new = 1,
+ ID_file_open,
+ ID_file_save,
+ ID_file_properties,
+ ID_jobs_make_dcp,
++ ID_jobs_make_kdms,
+ ID_jobs_send_dcp_to_tms,
+ ID_jobs_show_dcp,
+ };
+
+ void
+ setup_menu (wxMenuBar* m)
+ {
+ wxMenu* file = new wxMenu;
+ add_item (file, _("New..."), ID_file_new, ALWAYS);
+ add_item (file, _("&Open..."), ID_file_open, ALWAYS);
+ file->AppendSeparator ();
+ add_item (file, _("&Save"), ID_file_save, NEEDS_FILM);
+ file->AppendSeparator ();
+ add_item (file, _("&Properties..."), ID_file_properties, NEEDS_FILM);
+ #ifndef __WXOSX__
+ file->AppendSeparator ();
+ #endif
+
+ #ifdef __WXOSX__
+ add_item (file, _("&Exit"), wxID_EXIT, ALWAYS);
+ #else
+ add_item (file, _("&Quit"), wxID_EXIT, ALWAYS);
+ #endif
+
+
+ #ifdef __WXOSX__
+ add_item (file, _("&Preferences..."), wxID_PREFERENCES, ALWAYS);
+ #else
+ wxMenu* edit = new wxMenu;
+ add_item (edit, _("&Preferences..."), wxID_PREFERENCES, ALWAYS);
+ #endif
+
+ jobs_menu = new wxMenu;
+ add_item (jobs_menu, _("&Make DCP"), ID_jobs_make_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION);
++ add_item (jobs_menu, _("Make &KDMs..."), ID_jobs_make_kdms, NEEDS_FILM);
+ add_item (jobs_menu, _("&Send DCP to TMS"), ID_jobs_send_dcp_to_tms, NEEDS_FILM | NOT_DURING_DCP_CREATION);
+ add_item (jobs_menu, _("S&how DCP"), ID_jobs_show_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION);
+
+ wxMenu* help = new wxMenu;
+ #ifdef __WXOSX__
+ add_item (help, _("About DCP-o-matic"), wxID_ABOUT, ALWAYS);
+ #else
+ add_item (help, _("About"), wxID_ABOUT, ALWAYS);
+ #endif
+
+ m->Append (file, _("&File"));
+ #ifndef __WXOSX__
+ m->Append (edit, _("&Edit"));
+ #endif
+ m->Append (jobs_menu, _("&Jobs"));
+ m->Append (help, _("&Help"));
+ }
+
+ class Frame : public wxFrame
+ {
+ public:
+ Frame (wxString const & title)
+ : wxFrame (NULL, -1, title)
+ {
+ wxMenuBar* bar = new wxMenuBar;
+ setup_menu (bar);
+ SetMenuBar (bar);
+
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_new, this), ID_file_new);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_open, this), ID_file_open);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_save, this), ID_file_save);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_properties, this), ID_file_properties);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_exit, this), wxID_EXIT);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::edit_preferences, this), wxID_PREFERENCES);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_make_dcp, this), ID_jobs_make_dcp);
++ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_make_kdms, this), ID_jobs_make_kdms);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_send_dcp_to_tms, this), ID_jobs_send_dcp_to_tms);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_show_dcp, this), ID_jobs_show_dcp);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::help_about, this), wxID_ABOUT);
+
+ Bind (wxEVT_MENU_OPEN, boost::bind (&Frame::menu_opened, this, _1));
+ Bind (wxEVT_CLOSE_WINDOW, boost::bind (&Frame::close, this, _1));
+
+ /* Use a panel as the only child of the Frame so that we avoid
+ the dark-grey background on Windows.
+ */
+ wxPanel* overall_panel = new wxPanel (this, wxID_ANY);
+
+ film_editor = new FilmEditor (film, overall_panel);
+ film_viewer = new FilmViewer (film, overall_panel);
+ JobManagerView* job_manager_view = new JobManagerView (overall_panel, static_cast<JobManagerView::Buttons> (0));
+
+ wxBoxSizer* right_sizer = new wxBoxSizer (wxVERTICAL);
+ right_sizer->Add (film_viewer, 2, wxEXPAND | wxALL, 6);
+ right_sizer->Add (job_manager_view, 1, wxEXPAND | wxALL, 6);
+
+ wxBoxSizer* main_sizer = new wxBoxSizer (wxHORIZONTAL);
+ main_sizer->Add (film_editor, 1, wxEXPAND | wxALL, 6);
+ main_sizer->Add (right_sizer, 2, wxEXPAND | wxALL, 6);
+
+ set_menu_sensitivity ();
+
+ film_editor->FileChanged.connect (bind (&Frame::file_changed, this, _1));
+ if (film) {
+ file_changed (film->directory ());
+ } else {
+ file_changed ("");
+ }
+
+ JobManager::instance()->ActiveJobsChanged.connect (boost::bind (set_menu_sensitivity));
+
+ set_film ();
+ overall_panel->SetSizer (main_sizer);
+ }
+
+ private:
+
+ void menu_opened (wxMenuEvent& ev)
+ {
+ if (ev.GetMenu() != jobs_menu) {
+ return;
+ }
+
++ bool const have_dcp = false;//film && film->have_dcp();
+ jobs_menu->Enable (ID_jobs_send_dcp_to_tms, have_dcp);
+ jobs_menu->Enable (ID_jobs_show_dcp, have_dcp);
+ }
+
+ void set_film ()
+ {
+ film_viewer->set_film (film);
+ film_editor->set_film (film);
+ set_menu_sensitivity ();
+ }
+
+ void file_changed (string f)
+ {
+ stringstream s;
+ s << wx_to_std (_("DCP-o-matic"));
+ if (!f.empty ()) {
+ s << " - " << f;
+ }
+
+ SetTitle (std_to_wx (s.str()));
+ }
+
+ void file_new ()
+ {
+ NewFilmDialog* d = new NewFilmDialog (this);
+ int const r = d->ShowModal ();
+
+ if (r == wxID_OK) {
+
+ if (boost::filesystem::is_directory (d->get_path()) && !boost::filesystem::is_empty(d->get_path())) {
+ if (!confirm_dialog (
+ this,
+ std_to_wx (
+ String::compose (wx_to_std (_("The directory %1 already exists and is not empty. "
+ "Are you sure you want to use it?")),
+ d->get_path().c_str())
+ )
+ )) {
+ return;
+ }
+ } else if (boost::filesystem::is_regular_file (d->get_path())) {
+ error_dialog (
+ this,
+ String::compose (wx_to_std (_("%1 already exists as a file, so you cannot use it for a new film.")), d->get_path().c_str())
+ );
+ return;
+ }
+
+ maybe_save_then_delete_film ();
+ film.reset (new Film (d->get_path ()));
+ film->write_metadata ();
+ film->log()->set_level (log_level);
+ film->set_name (boost::filesystem::path (d->get_path()).filename().generic_string());
+ set_film ();
+ }
+
+ d->Destroy ();
+ }
+
+ void file_open ()
+ {
+ wxDirDialog* c = new wxDirDialog (this, _("Select film to open"), wxStandardPaths::Get().GetDocumentsDir(), wxDEFAULT_DIALOG_STYLE | wxDD_DIR_MUST_EXIST);
+ int r;
+ while (1) {
+ r = c->ShowModal ();
+ if (r == wxID_OK && c->GetPath() == wxStandardPaths::Get().GetDocumentsDir()) {
+ error_dialog (this, _("You did not select a folder. Make sure that you select a folder before clicking Open."));
+ } else {
+ break;
+ }
+ }
+
+ if (r == wxID_OK) {
+ maybe_save_then_delete_film ();
+ try {
+ film.reset (new Film (wx_to_std (c->GetPath ())));
+ film->read_metadata ();
+ film->log()->set_level (log_level);
+ set_film ();
+ } catch (std::exception& e) {
+ wxString p = c->GetPath ();
+ wxCharBuffer b = p.ToUTF8 ();
+ error_dialog (this, wxString::Format (_("Could not open film at %s (%s)"), p.data(), std_to_wx (e.what()).data()));
+ }
+ }
+
+ c->Destroy ();
+ }
+
+ void file_save ()
+ {
+ film->write_metadata ();
+ }
+
+ void file_properties ()
+ {
+ PropertiesDialog* d = new PropertiesDialog (this, film);
+ d->ShowModal ();
+ d->Destroy ();
+ }
+
+ void file_exit ()
+ {
+ if (!should_close ()) {
+ return;
+ }
+
+ maybe_save_then_delete_film ();
+ Close (true);
+ }
+
+ void edit_preferences ()
+ {
+ ConfigDialog* d = new ConfigDialog (this);
+ d->ShowModal ();
+ d->Destroy ();
+ Config::instance()->write ();
+ }
+
+ void jobs_make_dcp ()
+ {
+ JobWrapper::make_dcp (this, film);
+ }
++
++ void jobs_make_kdms ()
++ {
++ if (!film) {
++ return;
++ }
++
++ KDMDialog* d = new KDMDialog (this);
++ if (d->ShowModal () == wxID_OK) {
++ try {
++ film->make_kdms (
++ d->screens (),
++ d->from (),
++ d->until (),
++ d->directory ()
++ );
++ } catch (KDMError& e) {
++ error_dialog (this, e.what ());
++ }
++ }
++
++ d->Destroy ();
++ }
+
+ void jobs_send_dcp_to_tms ()
+ {
+ film->send_dcp_to_tms ();
+ }
+
+ void jobs_show_dcp ()
+ {
+ #ifdef __WXMSW__
+ string d = film->directory();
+ wstring w;
+ w.assign (d.begin(), d.end());
+ ShellExecute (0, L"open", w.c_str(), 0, 0, SW_SHOWDEFAULT);
+ #else
+ int r = system ("which nautilus");
+ if (WEXITSTATUS (r) == 0) {
+ r = system (string ("nautilus " + film->directory()).c_str ());
+ if (WEXITSTATUS (r)) {
+ error_dialog (this, _("Could not show DCP (could not run nautilus)"));
+ }
+ } else {
+ int r = system ("which konqueror");
+ if (WEXITSTATUS (r) == 0) {
+ r = system (string ("konqueror " + film->directory()).c_str ());
+ if (WEXITSTATUS (r)) {
+ error_dialog (this, _("Could not show DCP (could not run konqueror)"));
+ }
+ }
+ }
+ #endif
+ }
+
+ void help_about ()
+ {
+ AboutDialog* d = new AboutDialog (this);
+ d->ShowModal ();
+ d->Destroy ();
+ }
+
+ bool should_close ()
+ {
+ if (!JobManager::instance()->work_to_do ()) {
+ return true;
+ }
+
+ wxMessageDialog* d = new wxMessageDialog (
+ 0,
+ _("There are unfinished jobs; are you sure you want to quit?"),
+ _("Unfinished jobs"),
+ wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION
+ );
+
+ bool const r = d->ShowModal() == wxID_YES;
+ d->Destroy ();
+ return r;
+ }
+
+ void close (wxCloseEvent& ev)
+ {
+ if (!should_close ()) {
+ ev.Veto ();
+ return;
+ }
+
+ ev.Skip ();
+ }
+ };
+
+ #if wxMINOR_VERSION == 9
+ static const wxCmdLineEntryDesc command_line_description[] = {
+ { wxCMD_LINE_OPTION, "l", "log", "set log level (silent, verbose or timing)", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_SWITCH, "n", "new", "create new film", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_PARAM, 0, 0, "film to load or create", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 }
+ };
+ #else
+ static const wxCmdLineEntryDesc command_line_description[] = {
+ { wxCMD_LINE_OPTION, wxT("l"), wxT("log"), wxT("set log level (silent, verbose or timing)"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_SWITCH, wxT("n"), wxT("new"), wxT("create new film"), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_PARAM, 0, 0, wxT("film to load or create"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_NONE, wxT(""), wxT(""), wxT(""), wxCmdLineParamType (0), 0 }
+ };
+ #endif
+
+ class App : public wxApp
+ {
+ bool OnInit ()
+ try
+ {
+ if (!wxApp::OnInit()) {
+ return false;
+ }
+
+ #ifdef DCPOMATIC_LINUX
+ unsetenv ("UBUNTU_MENUPROXY");
+ #endif
+
+ #ifdef __WXOSX__
+ ProcessSerialNumber serial;
+ GetCurrentProcess (&serial);
+ TransformProcessType (&serial, kProcessTransformToForegroundApplication);
+ #endif
+
+ wxInitAllImageHandlers ();
+
+ /* Enable i18n; this will create a Config object
+ to look for a force-configured language. This Config
+ object will be wrong, however, because dcpomatic_setup
+ hasn't yet been called and there aren't any scalers, filters etc.
+ set up yet.
+ */
+ dcpomatic_setup_i18n ();
+
+ /* Set things up, including scalers / filters etc.
+ which will now be internationalised correctly.
+ */
+ dcpomatic_setup ();
+
+ /* Force the configuration to be re-loaded correctly next
+ time it is needed.
+ */
+ Config::drop ();
+
+ if (!film_to_load.empty() && boost::filesystem::is_directory (film_to_load)) {
+ try {
+ film.reset (new Film (film_to_load));
+ film->read_metadata ();
+ film->log()->set_level (log_level);
+ } catch (exception& e) {
+ error_dialog (0, std_to_wx (String::compose (wx_to_std (_("Could not load film %1 (%2)")), film_to_load, e.what())));
+ }
+ }
+
+ if (!film_to_create.empty ()) {
+ film.reset (new Film (film_to_create));
+ film->write_metadata ();
+ film->log()->set_level (log_level);
+ film->set_name (boost::filesystem::path (film_to_create).filename().generic_string ());
+ }
+
+ Frame* f = new Frame (_("DCP-o-matic"));
+ SetTopWindow (f);
+ f->Maximize ();
+ f->Show ();
+
+ ui_signaller = new wxUISignaller (this);
+ this->Bind (wxEVT_IDLE, boost::bind (&App::idle, this));
+
+ return true;
+ }
+ catch (exception& e)
+ {
+ error_dialog (0, wxString::Format ("DCP-o-matic could not start: %s", e.what ()));
+ return true;
+ }
+
+ void OnInitCmdLine (wxCmdLineParser& parser)
+ {
+ parser.SetDesc (command_line_description);
+ parser.SetSwitchChars (wxT ("-"));
+ }
+
+ bool OnCmdLineParsed (wxCmdLineParser& parser)
+ {
+ if (parser.GetParamCount() > 0) {
+ if (parser.Found (wxT ("new"))) {
+ film_to_create = wx_to_std (parser.GetParam (0));
+ } else {
+ film_to_load = wx_to_std (parser.GetParam(0));
+ }
+ }
+
+ wxString log;
+ if (parser.Found (wxT ("log"), &log)) {
+ log_level = wx_to_std (log);
+ }
+
+ return true;
+ }
+
+ void idle ()
+ {
+ ui_signaller->ui_idle ();
+ }
+ };
+
+ IMPLEMENT_APP (App)
--- /dev/null
- add_label_to_sizer (table, this, "Name");
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "cinema_dialog.h"
+#include "wx_util.h"
+
+using std::string;
+
+CinemaDialog::CinemaDialog (wxWindow* parent, string title, string name, string email)
+ : wxDialog (parent, wxID_ANY, std_to_wx (title))
+{
+ wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6);
+ table->AddGrowableCol (1, 1);
+
- add_label_to_sizer (table, this, "Email address for KDM delivery");
++ add_label_to_sizer (table, this, "Name", true);
+ _name = new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (256, -1));
+ table->Add (_name, 1, wxEXPAND);
+
++ add_label_to_sizer (table, this, "Email address for KDM delivery", true);
+ _email = new wxTextCtrl (this, wxID_ANY, std_to_wx (email), wxDefaultPosition, wxSize (256, -1));
+ table->Add (_email, 1, wxEXPAND);
+
+ wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+ overall_sizer->Add (table, 1, wxEXPAND | wxALL, 6);
+
+ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+ if (buttons) {
+ overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ SetSizer (overall_sizer);
+ overall_sizer->Layout ();
+ overall_sizer->SetSizeHints (this);
+}
+
+string
+CinemaDialog::name () const
+{
+ return wx_to_std (_name->GetValue());
+}
+
+string
+CinemaDialog::email () const
+{
+ return wx_to_std (_email->GetValue());
+}
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2013 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 <iomanip>
#include <wx/wx.h>
#include <wx/notebook.h>
+ #include <wx/listctrl.h>
#include <boost/thread.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
- #include "lib/format.h"
#include "lib/film.h"
#include "lib/transcode_job.h"
#include "lib/exceptions.h"
- #include "lib/ab_transcode_job.h"
#include "lib/job_manager.h"
#include "lib/filter.h"
+ #include "lib/ratio.h"
#include "lib/config.h"
- #include "lib/ffmpeg_decoder.h"
- #include "lib/external_audio_decoder.h"
- #include "filter_dialog.h"
+ #include "lib/still_image_content.h"
+ #include "lib/moving_image_content.h"
+ #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 "timecode.h"
#include "wx_util.h"
#include "film_editor.h"
- #include "gain_calculator_dialog.h"
- #include "sound_processor.h"
- #include "dci_name_dialog.h"
- #include "scaler.h"
+ #include "dci_metadata_dialog.h"
+ #include "timeline_dialog.h"
+ #include "timing_panel.h"
+ #include "subtitle_panel.h"
+ #include "audio_panel.h"
+ #include "video_panel.h"
using std::string;
using std::cout;
using std::setprecision;
using std::list;
using std::vector;
+ using std::max;
using boost::shared_ptr;
+ using boost::weak_ptr;
using boost::dynamic_pointer_cast;
+ using boost::lexical_cast;
/** @param f Film to edit */
FilmEditor::FilmEditor (shared_ptr<Film> f, wxWindow* parent)
: wxPanel (parent)
- , _film (f)
+ , _menu (f, this)
, _generally_sensitive (true)
+ , _timeline_dialog (0)
{
wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
- SetSizer (s);
- _notebook = new wxNotebook (this, wxID_ANY);
- s->Add (_notebook, 1);
-
- make_film_panel ();
- _notebook->AddPage (_film_panel, _("Film"), true);
- make_video_panel ();
- _notebook->AddPage (_video_panel, _("Video"), false);
- make_audio_panel ();
- _notebook->AddPage (_audio_panel, _("Audio"), false);
- make_subtitle_panel ();
- _notebook->AddPage (_subtitle_panel, _("Subtitles"), false);
-
- set_film (_film);
+
+ _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);
+
+ set_film (f);
connect_to_widgets ();
JobManager::instance()->ActiveJobsChanged.connect (
bind (&FilmEditor::active_jobs_changed, this, _1)
);
- setup_visibility ();
- setup_formats ();
+ SetSizerAndFit (s);
}
void
- FilmEditor::make_film_panel ()
+ FilmEditor::make_dcp_panel ()
{
- _film_panel = new wxPanel (_notebook);
- _film_sizer = new wxFlexGridSizer (2, 4, 4);
- wxBoxSizer* pad = new wxBoxSizer (wxVERTICAL);
- pad->Add (_film_sizer, 0, wxALL, 8);
- _film_panel->SetSizer (pad);
-
- add_label_to_sizer (_film_sizer, _film_panel, "Name");
- _name = new wxTextCtrl (_film_panel, wxID_ANY);
- _film_sizer->Add (_name, 1, wxEXPAND);
-
- add_label_to_sizer (_film_sizer, _film_panel, "DCP Name");
- _dcp_name = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
- _film_sizer->Add (_dcp_name, 0, wxALIGN_CENTER_VERTICAL | wxSHRINK);
-
- _use_dci_name = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Use DCI name"));
- _film_sizer->Add (_use_dci_name, 1, wxEXPAND);
- _edit_dci_button = new wxButton (_film_panel, wxID_ANY, wxT ("Details..."));
- _film_sizer->Add (_edit_dci_button, 0);
-
- add_label_to_sizer (_film_sizer, _film_panel, "Content");
- _content = new wxFilePickerCtrl (_film_panel, wxID_ANY, wxT (""), wxT ("Select Content File"), wxT("*.*"));
- _film_sizer->Add (_content, 1, wxEXPAND);
-
- _trust_content_header = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Trust content's header"));
- video_control (_trust_content_header);
- _film_sizer->Add (_trust_content_header, 1);
- _film_sizer->AddSpacer (0);
-
- add_label_to_sizer (_film_sizer, _film_panel, "Content Type");
- _dcp_content_type = new wxComboBox (_film_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
- _film_sizer->Add (_dcp_content_type);
-
- video_control (add_label_to_sizer (_film_sizer, _film_panel, "Frames Per Second"));
- _frames_per_second = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
- _film_sizer->Add (video_control (_frames_per_second), 1, wxALIGN_CENTER_VERTICAL);
+ _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;
- video_control (add_label_to_sizer (_film_sizer, _film_panel, "Original Size"));
- _original_size = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
- _film_sizer->Add (video_control (_original_size), 1, wxALIGN_CENTER_VERTICAL);
+ 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;
- video_control (add_label_to_sizer (_film_sizer, _film_panel, "Length"));
- _length = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
- _film_sizer->Add (video_control (_length), 1, wxALIGN_CENTER_VERTICAL);
-
+ 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_dci_name = new wxCheckBox (_dcp_panel, wxID_ANY, _("Use DCI name"));
+ grid->Add (_use_dci_name, wxGBPosition (r, 0), wxDefaultSpan, flags);
+ _edit_dci_button = new wxButton (_dcp_panel, wxID_ANY, _("Details..."));
+ grid->Add (_edit_dci_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;
{
- video_control (add_label_to_sizer (_film_sizer, _film_panel, "Trim frames"));
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Frame Rate"), true, wxGBPosition (r, 0));
wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- video_control (add_label_to_sizer (s, _film_panel, "Start"));
- _dcp_trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (video_control (_dcp_trim_start));
- video_control (add_label_to_sizer (s, _film_panel, "End"));
- _dcp_trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (video_control (_dcp_trim_end));
-
- _film_sizer->Add (s);
+ _frame_rate = new wxChoice (_dcp_panel, wxID_ANY);
+ s->Add (_frame_rate, 1, wxALIGN_CENTER_VERTICAL);
+ _best_frame_rate = new wxButton (_dcp_panel, wxID_ANY, _("Use best"));
+ s->Add (_best_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
+ grid->Add (s, wxGBPosition (r, 1));
}
+ ++r;
- _encrypted = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Encrypted"));
- _film_sizer->Add (_encrypted, 1);
- _film_sizer->AddSpacer (0);
++ _encrypted = new wxCheckBox (_dcp_panel, wxID_ANY, wxT ("Encrypted"));
++ grid->Add (_encrypted, wxGBPosition (r, 0), wxGBSpan (1, 2));
++ ++r;
+
- _multiple_reels = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Make multiple reels"));
- _film_sizer->Add (_multiple_reels);
+ 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;
- {
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _reel_size = new wxSpinCtrl (_film_panel, wxID_ANY);
- s->Add (_reel_size);
- add_label_to_sizer (s, _film_panel, "Gb each");
- _film_sizer->Add (s);
- }
+ _three_d = new wxCheckBox (_dcp_panel, wxID_ANY, _("3D"));
+ grid->Add (_three_d, wxGBPosition (r, 0), wxGBSpan (1, 2));
+ ++r;
- _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, wxT ("A/B"));
- video_control (_dcp_ab);
- _film_sizer->Add (_dcp_ab, 1);
- _film_sizer->AddSpacer (0);
+ 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;
- /* STILL-only stuff */
{
- still_control (add_label_to_sizer (_film_sizer, _film_panel, "Duration"));
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("JPEG2000 bandwidth"), true, wxGBPosition (r, 0));
wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _still_duration = new wxSpinCtrl (_film_panel);
- still_control (_still_duration);
- s->Add (_still_duration, 1, wxEXPAND);
- still_control (add_label_to_sizer (s, _film_panel, "s"));
- _film_sizer->Add (s);
- }
-
- 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 ()));
- }
-
- _reel_size->SetRange(1, 1000);
- }
-
- void
- FilmEditor::connect_to_widgets ()
- {
- _name->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (FilmEditor::name_changed), 0, this);
- _use_dci_name->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::use_dci_name_toggled), 0, this);
- _edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this);
- _format->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this);
- _content->Connect (wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::content_changed), 0, this);
- _trust_content_header->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::trust_content_header_changed), 0, this);
- _left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this);
- _right_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::right_crop_changed), 0, this);
- _top_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::top_crop_changed), 0, this);
- _bottom_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::bottom_crop_changed), 0, this);
- _filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_filters_clicked), 0, this);
- _scaler->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this);
- _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this);
- _dcp_ab->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::dcp_ab_toggled), 0, this);
- _encrypted->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::encrypted_toggled), 0, this);
- _still_duration->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this);
- _dcp_trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_start_changed), 0, this);
- _dcp_trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_end_changed), 0, this);
- _multiple_reels->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::multiple_reels_toggled), 0, this);
- _reel_size->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::reel_size_changed), 0, this);
- _with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this);
- _subtitle_offset->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this);
- _subtitle_scale->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_scale_changed), 0, this);
- _colour_lut->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::colour_lut_changed), 0, this);
- _j2k_bandwidth->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::j2k_bandwidth_changed), 0, this);
- _subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this);
- _audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this);
- _audio_gain->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_gain_changed), 0, this);
- _audio_gain_calculate_button->Connect (
- wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::audio_gain_calculate_button_clicked), 0, this
- );
- _audio_delay->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this);
- _use_content_audio->Connect (wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler (FilmEditor::use_audio_changed), 0, this);
- _use_external_audio->Connect (wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler (FilmEditor::use_audio_changed), 0, this);
- for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
- _external_audio[i]->Connect (
- wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::external_audio_changed), 0, this
- );
- }
- }
-
- void
- FilmEditor::make_video_panel ()
- {
- _video_panel = new wxPanel (_notebook);
- _video_sizer = new wxFlexGridSizer (2, 4, 4);
- wxBoxSizer* pad = new wxBoxSizer (wxVERTICAL);
- pad->Add (_video_sizer, 0, wxALL, 8);
- _video_panel->SetSizer (pad);
-
- add_label_to_sizer (_video_sizer, _video_panel, "Format");
- _format = new wxComboBox (_video_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
- _video_sizer->Add (_format);
-
- {
- add_label_to_sizer (_video_sizer, _video_panel, "Crop");
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-
- add_label_to_sizer (s, _video_panel, "L");
- _left_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (_left_crop, 0);
- add_label_to_sizer (s, _video_panel, "R");
- _right_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (_right_crop, 0);
- add_label_to_sizer (s, _video_panel, "T");
- _top_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (_top_crop, 0);
- add_label_to_sizer (s, _video_panel, "B");
- _bottom_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (_bottom_crop, 0);
-
- _video_sizer->Add (s);
+ _j2k_bandwidth = new wxSpinCtrl (_dcp_panel, wxID_ANY);
+ s->Add (_j2k_bandwidth, 1);
+ add_label_to_sizer (s, _dcp_panel, _("MBps"), false);
+ grid->Add (s, wxGBPosition (r, 1));
}
+ ++r;
- /* VIDEO-only stuff */
- {
- video_control (add_label_to_sizer (_video_sizer, _video_panel, "Filters"));
- wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _filters = new wxStaticText (_video_panel, wxID_ANY, wxT ("None"));
- video_control (_filters);
- s->Add (_filters, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM | wxRIGHT, 6);
- _filters_button = new wxButton (_video_panel, wxID_ANY, wxT ("Edit..."));
- video_control (_filters_button);
- s->Add (_filters_button, 0);
- _video_sizer->Add (s, 1);
- }
+ 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;
- video_control (add_label_to_sizer (_video_sizer, _video_panel, "Scaler"));
- _scaler = new wxComboBox (_video_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
- _video_sizer->Add (video_control (_scaler), 1);
+ 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()));
}
- add_label_to_sizer (_video_sizer, _video_panel, "Colour look-up table");
- _colour_lut = new wxComboBox (_video_panel, wxID_ANY);
- for (int i = 0; i < 2; ++i) {
- _colour_lut->Append (std_to_wx (colour_lut_index_to_name (i)));
- }
- _colour_lut->SetSelection (0);
- _video_sizer->Add (_colour_lut, 1, wxEXPAND);
-
- {
- add_label_to_sizer (_video_sizer, _video_panel, "JPEG2000 bandwidth");
- wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _j2k_bandwidth = new wxSpinCtrl (_video_panel, wxID_ANY);
- s->Add (_j2k_bandwidth, 1);
- add_label_to_sizer (s, _video_panel, "MBps");
- _video_sizer->Add (s, 1);
+ 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 ()));
}
- _left_crop->SetRange (0, 1024);
- _top_crop->SetRange (0, 1024);
- _right_crop->SetRange (0, 1024);
- _bottom_crop->SetRange (0, 1024);
- _still_duration->SetRange (1, 60 * 60);
- _dcp_trim_start->SetRange (0, 100);
- _dcp_trim_end->SetRange (0, 100);
- _j2k_bandwidth->SetRange (50, 250);
- }
-
- void
- FilmEditor::make_audio_panel ()
- {
- _audio_panel = new wxPanel (_notebook);
- _audio_sizer = new wxFlexGridSizer (2, 4, 4);
- wxBoxSizer* pad = new wxBoxSizer (wxVERTICAL);
- pad->Add (_audio_sizer, 0, wxALL, 8);
- _audio_panel->SetSizer (pad);
-
- {
- video_control (add_label_to_sizer (_audio_sizer, _audio_panel, "Audio Gain"));
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _audio_gain = new wxSpinCtrl (_audio_panel);
- s->Add (video_control (_audio_gain), 1);
- video_control (add_label_to_sizer (s, _audio_panel, "dB"));
- _audio_gain_calculate_button = new wxButton (_audio_panel, wxID_ANY, _("Calculate..."));
- video_control (_audio_gain_calculate_button);
- s->Add (_audio_gain_calculate_button, 1, wxEXPAND);
- _audio_sizer->Add (s);
+ 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 ()));
}
- {
- video_control (add_label_to_sizer (_audio_sizer, _audio_panel, "Audio Delay"));
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _audio_delay = new wxSpinCtrl (_audio_panel);
- s->Add (video_control (_audio_delay), 1);
- video_control (add_label_to_sizer (s, _audio_panel, "ms"));
- _audio_sizer->Add (s);
+ list<int> const dfr = Config::instance()->allowed_dcp_frame_rates ();
+ for (list<int>::const_iterator i = dfr.begin(); i != dfr.end(); ++i) {
+ _frame_rate->Append (std_to_wx (boost::lexical_cast<string> (*i)));
}
- {
- _use_content_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use content's audio"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
- _audio_sizer->Add (video_control (_use_content_audio));
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _audio_stream = new wxComboBox (_audio_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
- s->Add (video_control (_audio_stream), 1);
- _audio = new wxStaticText (_audio_panel, wxID_ANY, wxT (""));
- s->Add (video_control (_audio), 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 8);
- _audio_sizer->Add (s, 1, wxEXPAND);
- }
+ _audio_channels->SetRange (0, MAX_AUDIO_CHANNELS);
+ _j2k_bandwidth->SetRange (1, 250);
- _use_external_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use external audio"));
- _audio_sizer->Add (_use_external_audio);
- _audio_sizer->AddSpacer (0);
-
- assert (MAX_AUDIO_CHANNELS == 6);
-
- char const * channels[] = {
- "Left",
- "Right",
- "Centre",
- "Lfe (sub)",
- "Left surround",
- "Right surround"
- };
-
- for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
- add_label_to_sizer (_audio_sizer, _audio_panel, channels[i]);
- _external_audio[i] = new wxFilePickerCtrl (_audio_panel, wxID_ANY, wxT (""), wxT ("Select Audio File"), wxT ("*.wav"));
- _audio_sizer->Add (_external_audio[i], 1, wxEXPAND);
- }
+ _resolution->Append (_("2K"));
+ _resolution->Append (_("4K"));
- _audio_gain->SetRange (-60, 60);
- _audio_delay->SetRange (-1000, 1000);
+ _standard->Append (_("SMPTE"));
+ _standard->Append (_("Interop"));
}
void
- FilmEditor::make_subtitle_panel ()
+ FilmEditor::connect_to_widgets ()
{
- _subtitle_panel = new wxPanel (_notebook);
- _subtitle_sizer = new wxFlexGridSizer (2, 4, 4);
- wxBoxSizer* pad = new wxBoxSizer (wxVERTICAL);
- pad->Add (_subtitle_sizer, 0, wxALL, 8);
- _subtitle_panel->SetSizer (pad);
-
- _with_subtitles = new wxCheckBox (_subtitle_panel, wxID_ANY, wxT("With Subtitles"));
- video_control (_with_subtitles);
- _subtitle_sizer->Add (_with_subtitles, 1);
-
- _subtitle_stream = new wxComboBox (_subtitle_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
- _subtitle_sizer->Add (video_control (_subtitle_stream));
-
- video_control (add_label_to_sizer (_subtitle_sizer, _subtitle_panel, "Subtitle Offset"));
- _subtitle_offset = new wxSpinCtrl (_subtitle_panel);
- _subtitle_sizer->Add (video_control (_subtitle_offset), 1);
+ _name->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&FilmEditor::name_changed, this));
+ _use_dci_name->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::use_dci_name_toggled, this));
+ _edit_dci_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::edit_dci_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_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->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::frame_rate_changed, this));
+ _best_frame_rate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::best_frame_rate_clicked, 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);
{
- video_control (add_label_to_sizer (_subtitle_sizer, _subtitle_panel, "Subtitle Scale"));
wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _subtitle_scale = new wxSpinCtrl (_subtitle_panel);
- s->Add (video_control (_subtitle_scale));
- video_control (add_label_to_sizer (s, _subtitle_panel, "%"));
- _subtitle_sizer->Add (s);
- }
-
- _subtitle_offset->SetRange (-1024, 1024);
- _subtitle_scale->SetRange (1, 1000);
- }
-
- /** Called when the left crop widget has been changed */
- void
- FilmEditor::left_crop_changed (wxCommandEvent &)
- {
- if (!_film) {
- return;
- }
-
- _film->set_left_crop (_left_crop->GetValue ());
- }
-
- /** Called when the right crop widget has been changed */
- void
- FilmEditor::right_crop_changed (wxCommandEvent &)
- {
- if (!_film) {
- return;
- }
-
- _film->set_right_crop (_right_crop->GetValue ());
- }
-
- /** Called when the top crop widget has been changed */
- void
- FilmEditor::top_crop_changed (wxCommandEvent &)
- {
- if (!_film) {
- return;
- }
+
+ _content = new wxListCtrl (_content_panel, wxID_ANY, wxDefaultPosition, wxSize (320, 160), wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL);
+ s->Add (_content, 1, wxEXPAND | wxTOP | wxBOTTOM, 6);
- _film->set_top_crop (_top_crop->GetValue ());
- }
+ _content->InsertColumn (0, wxT(""));
+ _content->SetColumnWidth (0, 512);
- /** Called when the bottom crop value has been changed */
- void
- FilmEditor::bottom_crop_changed (wxCommandEvent &)
- {
- if (!_film) {
- return;
- }
+ wxBoxSizer* b = new wxBoxSizer (wxVERTICAL);
+ _content_add_file = new wxButton (_content_panel, wxID_ANY, _("Add file(s)..."));
+ b->Add (_content_add_file, 1, wxEXPAND | wxLEFT | wxRIGHT);
+ _content_add_folder = new wxButton (_content_panel, wxID_ANY, _("Add folder..."));
+ b->Add (_content_add_folder, 1, wxEXPAND | wxLEFT | wxRIGHT);
+ _content_remove = new wxButton (_content_panel, wxID_ANY, _("Remove"));
+ b->Add (_content_remove, 1, wxEXPAND | wxLEFT | wxRIGHT);
+ _content_timeline = new wxButton (_content_panel, wxID_ANY, _("Timeline..."));
+ b->Add (_content_timeline, 1, wxEXPAND | wxLEFT | wxRIGHT);
- _film->set_bottom_crop (_bottom_crop->GetValue ());
- }
+ s->Add (b, 0, wxALL, 4);
- /** Called when the content filename has been changed */
- void
- FilmEditor::content_changed (wxCommandEvent &)
- {
- if (!_film) {
- return;
+ _content_sizer->Add (s, 0.75, wxEXPAND | wxALL, 6);
}
- try {
- _film->set_content (wx_to_std (_content->GetPath ()));
- } catch (std::exception& e) {
- _content->SetPath (std_to_wx (_film->directory ()));
- error_dialog (this, String::compose ("Could not set content: %1", e.what ()));
- }
- }
+ _sequence_video = new wxCheckBox (_content_panel, wxID_ANY, _("Keep video in sequence"));
+ _content_sizer->Add (_sequence_video);
- void
- FilmEditor::trust_content_header_changed (wxCommandEvent &)
- {
- if (!_film) {
- return;
- }
+ _content_notebook = new wxNotebook (_content_panel, wxID_ANY);
+ _content_sizer->Add (_content_notebook, 1, wxEXPAND | wxTOP, 6);
- _film->set_trust_content_header (_trust_content_header->GetValue ());
- }
-
- void
- FilmEditor::multiple_reels_toggled (wxCommandEvent &)
- {
- if (!_film) {
- return;
- }
-
- if (_multiple_reels->GetValue()) {
- _film->set_reel_size (_reel_size->GetValue() * 1e9);
- } else {
- _film->unset_reel_size ();
- }
-
- setup_reel_control_sensitivity ();
+ _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::reel_size_changed (wxCommandEvent &)
+ FilmEditor::name_changed ()
{
if (!_film) {
return;
}
- _film->set_reel_size (static_cast<uint64_t> (_reel_size->GetValue()) * 1e9);
+ _film->set_name (string (_name->GetValue().mb_str()));
}
- /** Called when the DCP A/B switch has been toggled */
void
- FilmEditor::dcp_ab_toggled (wxCommandEvent &)
+ FilmEditor::j2k_bandwidth_changed ()
{
if (!_film) {
return;
}
- _film->set_dcp_ab (_dcp_ab->GetValue ());
+ _film->set_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1e6);
}
- FilmEditor::encrypted_toggled (wxCommandEvent &)
+void
++FilmEditor::encrypted_toggled ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_encrypted (_encrypted->GetValue ());
+}
+
+/** Called when the name widget has been changed */
void
- FilmEditor::name_changed (wxCommandEvent &)
+ FilmEditor::frame_rate_changed ()
{
if (!_film) {
return;
}
- _film->set_name (string (_name->GetValue().mb_str()));
+ _film->set_video_frame_rate (
+ boost::lexical_cast<int> (
+ wx_to_std (_frame_rate->GetString (_frame_rate->GetSelection ()))
+ )
+ );
}
void
- FilmEditor::subtitle_offset_changed (wxCommandEvent &)
+ FilmEditor::audio_channels_changed ()
{
if (!_film) {
return;
}
- _film->set_subtitle_offset (_subtitle_offset->GetValue ());
+ _film->set_audio_channels (_audio_channels->GetValue ());
}
void
- FilmEditor::subtitle_scale_changed (wxCommandEvent &)
+ FilmEditor::resolution_changed ()
{
if (!_film) {
return;
}
- _film->set_subtitle_scale (_subtitle_scale->GetValue() / 100.0);
- }
-
- void
- FilmEditor::colour_lut_changed (wxCommandEvent &)
- {
- if (!_film) {
- return;
- }
-
- _film->set_colour_lut (_colour_lut->GetSelection ());
+ _film->set_resolution (_resolution->GetSelection() == 0 ? RESOLUTION_2K : RESOLUTION_4K);
}
void
- FilmEditor::j2k_bandwidth_changed (wxCommandEvent &)
+ FilmEditor::standard_changed ()
{
if (!_film) {
return;
}
-
- _film->set_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1e6);
- }
+ _film->set_interop (_standard->GetSelection() == 1);
+ }
/** Called when the metadata stored in the Film object has changed;
* so that we can update the GUI.
}
stringstream 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:
- checked_set (_content, _film->content ());
- setup_visibility ();
- setup_formats ();
- setup_subtitle_control_sensitivity ();
- setup_streams ();
+ setup_content ();
break;
- case Film::TRUST_CONTENT_HEADER:
- checked_set (_trust_content_header, _film->trust_content_header ());
+ case Film::CONTAINER:
+ setup_container ();
break;
- case Film::SUBTITLE_STREAMS:
- setup_subtitle_control_sensitivity ();
- setup_streams ();
- break;
- case Film::CONTENT_AUDIO_STREAMS:
- setup_streams ();
- break;
- case Film::FORMAT:
- {
- int n = 0;
- vector<Format const *>::iterator i = _formats.begin ();
- while (i != _formats.end() && *i != _film->format ()) {
- ++i;
- ++n;
- }
- if (i == _formats.end()) {
- checked_set (_format, -1);
- } else {
- checked_set (_format, n);
- }
- _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
- break;
- }
- case Film::CROP:
- checked_set (_left_crop, _film->crop().left);
- checked_set (_right_crop, _film->crop().right);
- checked_set (_top_crop, _film->crop().top);
- checked_set (_bottom_crop, _film->crop().bottom);
- break;
- case Film::FILTERS:
- {
- pair<string, string> p = Filter::ffmpeg_strings (_film->filters ());
- if (p.first.empty () && p.second.empty ()) {
- _filters->SetLabel (_("None"));
- } else {
- string const b = p.first + " " + p.second;
- _filters->SetLabel (std_to_wx (b));
- }
- _film_sizer->Layout ();
- break;
- }
case Film::NAME:
checked_set (_name, _film->name());
- _film->set_dci_date_today ();
- _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
- break;
- case Film::FRAMES_PER_SECOND:
- s << fixed << setprecision(2) << _film->frames_per_second();
- _frames_per_second->SetLabel (std_to_wx (s.str ()));
+ setup_dcp_name ();
break;
- case Film::SIZE:
- if (_film->size().width == 0 && _film->size().height == 0) {
- _original_size->SetLabel (wxT (""));
- } else {
- s << _film->size().width << " x " << _film->size().height;
- _original_size->SetLabel (std_to_wx (s.str ()));
- }
- break;
- case Film::LENGTH:
- if (_film->frames_per_second() > 0 && _film->length()) {
- s << _film->length().get() << " frames; " << seconds_to_hms (_film->length().get() / _film->frames_per_second());
- } else if (_film->length()) {
- s << _film->length().get() << " frames";
- }
- _length->SetLabel (std_to_wx (s.str ()));
- if (_film->length()) {
- _dcp_trim_start->SetRange (0, _film->length().get());
- _dcp_trim_end->SetRange (0, _film->length().get());
- }
+ 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 ()));
- _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
- break;
- case Film::DCP_AB:
- checked_set (_dcp_ab, _film->dcp_ab ());
+ setup_dcp_name ();
break;
case Film::SCALER:
checked_set (_scaler, Scaler::as_index (_film->scaler ()));
break;
- case Film::DCP_TRIM_START:
- checked_set (_dcp_trim_start, _film->dcp_trim_start());
- break;
- case Film::DCP_TRIM_END:
- checked_set (_dcp_trim_end, _film->dcp_trim_end());
- break;
- case Film::REEL_SIZE:
- if (_film->reel_size()) {
- checked_set (_multiple_reels, true);
- checked_set (_reel_size, _film->reel_size().get() / 1e9);
- } else {
- checked_set (_multiple_reels, false);
- }
- setup_reel_control_sensitivity ();
- break;
- case Film::AUDIO_GAIN:
- checked_set (_audio_gain, _film->audio_gain ());
- break;
- case Film::AUDIO_DELAY:
- checked_set (_audio_delay, _film->audio_delay ());
- break;
- case Film::STILL_DURATION:
- checked_set (_still_duration, _film->still_duration ());
- break;
- case Film::WITH_SUBTITLES:
- checked_set (_with_subtitles, _film->with_subtitles ());
- setup_subtitle_control_sensitivity ();
- _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
- break;
- case Film::SUBTITLE_OFFSET:
- checked_set (_subtitle_offset, _film->subtitle_offset ());
- break;
- case Film::SUBTITLE_SCALE:
- checked_set (_subtitle_scale, _film->subtitle_scale() * 100);
- break;
+ case Film::ENCRYPTED:
+ checked_set (_encrypted, _film->encrypted ());
+ break;
- case Film::COLOUR_LUT:
- checked_set (_colour_lut, _film->colour_lut ());
+ case Film::RESOLUTION:
+ checked_set (_resolution, _film->resolution() == RESOLUTION_2K ? 0 : 1);
+ setup_dcp_name ();
break;
case Film::J2K_BANDWIDTH:
checked_set (_j2k_bandwidth, double (_film->j2k_bandwidth()) / 1e6);
break;
case Film::USE_DCI_NAME:
checked_set (_use_dci_name, _film->use_dci_name ());
- _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+ setup_dcp_name ();
break;
case Film::DCI_METADATA:
- _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+ setup_dcp_name ();
break;
- case Film::CONTENT_AUDIO_STREAM:
- if (_film->content_audio_stream()) {
- checked_set (_audio_stream, _film->content_audio_stream()->to_string());
+ case Film::VIDEO_FRAME_RATE:
+ {
+ bool done = false;
+ for (unsigned int i = 0; i < _frame_rate->GetCount(); ++i) {
+ if (wx_to_std (_frame_rate->GetString(i)) == boost::lexical_cast<string> (_film->video_frame_rate())) {
+ checked_set (_frame_rate, i);
+ done = true;
+ break;
+ }
+ }
+
+ if (!done) {
+ checked_set (_frame_rate, -1);
}
- _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
- setup_audio_details ();
- setup_audio_control_sensitivity ();
+
+ _best_frame_rate->Enable (_film->best_video_frame_rate () != _film->video_frame_rate ());
break;
- case Film::USE_CONTENT_AUDIO:
- checked_set (_use_content_audio, _film->use_content_audio());
- checked_set (_use_external_audio, !_film->use_content_audio());
- _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
- setup_audio_details ();
- setup_audio_control_sensitivity ();
+ }
+ case Film::AUDIO_CHANNELS:
+ _audio_channels->SetValue (_film->audio_channels ());
+ setup_dcp_name ();
break;
- case Film::SUBTITLE_STREAM:
- if (_film->subtitle_stream()) {
- checked_set (_subtitle_stream, _film->subtitle_stream()->to_string());
- }
+ case Film::SEQUENCE_VIDEO:
+ checked_set (_sequence_video, _film->sequence_video ());
break;
- case Film::EXTERNAL_AUDIO:
- {
- vector<string> a = _film->external_audio ();
- for (size_t i = 0; i < a.size() && i < MAX_AUDIO_CHANNELS; ++i) {
- checked_set (_external_audio[i], a[i]);
- }
- setup_audio_details ();
+ 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;
}
+ }
+
+ void
+ FilmEditor::film_content_changed (weak_ptr<Content> weak_content, int property)
+ {
+ ensure_ui_thread ();
+
+ if (!_film) {
+ /* We call this method ourselves (as well as using it as a signal handler)
+ so _film can be 0.
+ */
+ return;
+ }
+
+ shared_ptr<Content> content = weak_content.lock ();
+ if (!content || content != selected_content ()) {
+ return;
+ }
+
+ for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
+ (*i)->film_content_changed (content, property);
+ }
+
+ if (property == FFmpegContentProperty::AUDIO_STREAM) {
+ setup_dcp_name ();
}
}
- /** Called when the format widget has been changed */
void
- FilmEditor::format_changed (wxCommandEvent &)
+ 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 = _format->GetSelection ();
+ int const n = _container->GetSelection ();
if (n >= 0) {
- assert (n < int (_formats.size()));
- _film->set_format (_formats[n]);
+ 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 (wxCommandEvent &)
+ FilmEditor::dcp_content_type_changed ()
{
if (!_film) {
return;
void
FilmEditor::set_film (shared_ptr<Film> f)
{
- _film = f;
+ set_general_sensitivity (f != 0);
- set_things_sensitive (_film != 0);
+ if (_film == f) {
+ return;
+ }
+
+ _film = f;
if (_film) {
_film->Changed.connect (bind (&FilmEditor::film_changed, this, _1));
+ _film->ContentChanged.connect (bind (&FilmEditor::film_content_changed, this, _1, _2));
}
if (_film) {
} else {
FileChanged ("");
}
-
+
film_changed (Film::NAME);
film_changed (Film::USE_DCI_NAME);
film_changed (Film::CONTENT);
- film_changed (Film::TRUST_CONTENT_HEADER);
film_changed (Film::DCP_CONTENT_TYPE);
- film_changed (Film::FORMAT);
- film_changed (Film::CROP);
- film_changed (Film::FILTERS);
+ film_changed (Film::CONTAINER);
+ film_changed (Film::RESOLUTION);
film_changed (Film::SCALER);
- film_changed (Film::DCP_TRIM_START);
- film_changed (Film::DCP_TRIM_END);
- film_changed (Film::REEL_SIZE);
- film_changed (Film::DCP_AB);
- film_changed (Film::CONTENT_AUDIO_STREAM);
- film_changed (Film::EXTERNAL_AUDIO);
- film_changed (Film::USE_CONTENT_AUDIO);
- film_changed (Film::AUDIO_GAIN);
- film_changed (Film::AUDIO_DELAY);
- film_changed (Film::STILL_DURATION);
film_changed (Film::WITH_SUBTITLES);
- film_changed (Film::SUBTITLE_OFFSET);
- film_changed (Film::SUBTITLE_SCALE);
+ film_changed (Film::ENCRYPTED);
- film_changed (Film::COLOUR_LUT);
film_changed (Film::J2K_BANDWIDTH);
film_changed (Film::DCI_METADATA);
- film_changed (Film::SIZE);
- film_changed (Film::LENGTH);
- film_changed (Film::CONTENT_AUDIO_STREAMS);
- film_changed (Film::SUBTITLE_STREAMS);
- film_changed (Film::FRAMES_PER_SECOND);
+ 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_selection_changed ();
}
- /** Updates the sensitivity of lots of widgets to a given value.
- * @param s true to make sensitive, false to make insensitive.
- */
void
- FilmEditor::set_things_sensitive (bool s)
+ FilmEditor::set_general_sensitivity (bool s)
{
_generally_sensitive = s;
-
+
+ /* Stuff in the Content / DCP tabs */
_name->Enable (s);
_use_dci_name->Enable (s);
_edit_dci_button->Enable (s);
- _format->Enable (s);
_content->Enable (s);
- _trust_content_header->Enable (s);
- _left_crop->Enable (s);
- _right_crop->Enable (s);
- _top_crop->Enable (s);
- _bottom_crop->Enable (s);
- _filters_button->Enable (s);
- _scaler->Enable (s);
- _audio_stream->Enable (s);
+ _content_add_file->Enable (s);
+ _content_add_folder->Enable (s);
+ _content_remove->Enable (s);
+ _content_timeline->Enable (s);
_dcp_content_type->Enable (s);
- _dcp_trim_start->Enable (s);
- _dcp_trim_end->Enable (s);
- _multiple_reels->Enable (s);
- _reel_size->Enable (s);
- _dcp_ab->Enable (s);
+ _encrypted->Enable (s);
- _colour_lut->Enable (s);
+ _frame_rate->Enable (s);
+ _audio_channels->Enable (s);
_j2k_bandwidth->Enable (s);
- _audio_gain->Enable (s);
- _audio_gain_calculate_button->Enable (s);
- _audio_delay->Enable (s);
- _still_duration->Enable (s);
-
- setup_subtitle_control_sensitivity ();
- setup_audio_control_sensitivity ();
- setup_reel_control_sensitivity ();
- }
+ _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);
- /** Called when the `Edit filters' button has been clicked */
- void
- FilmEditor::edit_filters_clicked (wxCommandEvent &)
- {
- FilterDialog* d = new FilterDialog (this, _film->filters());
- d->ActiveChanged.connect (bind (&Film::set_filters, _film, _1));
- d->ShowModal ();
- d->Destroy ();
+ /* 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 (wxCommandEvent &)
+ FilmEditor::scaler_changed ()
{
if (!_film) {
return;
}
void
- FilmEditor::audio_gain_changed (wxCommandEvent &)
+ FilmEditor::use_dci_name_toggled ()
{
if (!_film) {
return;
}
- _film->set_audio_gain (_audio_gain->GetValue ());
+ _film->set_use_dci_name (_use_dci_name->GetValue ());
}
void
- FilmEditor::audio_delay_changed (wxCommandEvent &)
+ FilmEditor::edit_dci_button_clicked ()
{
if (!_film) {
return;
}
- _film->set_audio_delay (_audio_delay->GetValue ());
- }
-
- wxControl *
- FilmEditor::video_control (wxControl* c)
- {
- _video_controls.push_back (c);
- return c;
- }
-
- wxControl *
- FilmEditor::still_control (wxControl* c)
- {
- _still_controls.push_back (c);
- return c;
+ DCIMetadataDialog* d = new DCIMetadataDialog (this, _film->dci_metadata ());
+ d->ShowModal ();
+ _film->set_dci_metadata (d->dci_metadata ());
+ d->Destroy ();
}
void
- FilmEditor::setup_visibility ()
+ FilmEditor::active_jobs_changed (bool a)
{
- ContentType c = VIDEO;
-
- if (_film) {
- c = _film->content_type ();
- }
-
- for (list<wxControl*>::iterator i = _video_controls.begin(); i != _video_controls.end(); ++i) {
- (*i)->Show (c == VIDEO);
- }
-
- for (list<wxControl*>::iterator i = _still_controls.begin(); i != _still_controls.end(); ++i) {
- (*i)->Show (c == STILL);
- }
-
- _notebook->InvalidateBestSize ();
-
- _film_sizer->Layout ();
- _film_sizer->SetSizeHints (_film_panel);
- _video_sizer->Layout ();
- _video_sizer->SetSizeHints (_video_panel);
- _audio_sizer->Layout ();
- _audio_sizer->SetSizeHints (_audio_panel);
- _subtitle_sizer->Layout ();
- _subtitle_sizer->SetSizeHints (_subtitle_panel);
-
- _notebook->Fit ();
- Fit ();
+ set_general_sensitivity (!a);
}
void
- FilmEditor::still_duration_changed (wxCommandEvent &)
+ FilmEditor::setup_dcp_name ()
{
- if (!_film) {
- return;
+ 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));
}
-
- _film->set_still_duration (_still_duration->GetValue ());
}
void
- FilmEditor::dcp_trim_start_changed (wxCommandEvent &)
+ FilmEditor::best_frame_rate_clicked ()
{
if (!_film) {
return;
}
-
- _film->set_dcp_trim_start (_dcp_trim_start->GetValue ());
+
+ _film->set_video_frame_rate (_film->best_video_frame_rate ());
}
void
- FilmEditor::dcp_trim_end_changed (wxCommandEvent &)
+ FilmEditor::setup_content ()
{
- if (!_film) {
- return;
+ 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 ();
+ for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
+ int const t = _content->GetItemCount ();
+ _content->InsertItem (t, std_to_wx ((*i)->summary ()));
+ if ((*i)->summary() == selected_summary) {
+ _content->SetItemState (t, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+ }
}
- _film->set_dcp_trim_end (_dcp_trim_end->GetValue ());
+ 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::audio_gain_calculate_button_clicked (wxCommandEvent &)
+ FilmEditor::content_add_file_clicked ()
{
- GainCalculatorDialog* d = new GainCalculatorDialog (this);
- d->ShowModal ();
+ wxFileDialog* d = new wxFileDialog (this, _("Choose a file or files"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE);
+ int const r = d->ShowModal ();
+ d->Destroy ();
- if (d->wanted_fader() == 0 || d->actual_fader() == 0) {
- d->Destroy ();
+ if (r != wxID_OK) {
return;
}
-
- _audio_gain->SetValue (
- Config::instance()->sound_processor()->db_for_fader_change (
- d->wanted_fader (),
- d->actual_fader ()
- )
- );
- /* This appears to be necessary, as the change is not signalled,
- I think.
- */
- wxCommandEvent dummy;
- audio_gain_changed (dummy);
-
- d->Destroy ();
- }
+ wxArrayString paths;
+ d->GetPaths (paths);
- void
- FilmEditor::setup_formats ()
- {
- ContentType c = VIDEO;
+ /* XXX: check for lots of files here and do something */
- if (_film) {
- c = _film->content_type ();
- }
-
- _formats.clear ();
-
- vector<Format const *> fmt = Format::all ();
- for (vector<Format const *>::iterator i = fmt.begin(); i != fmt.end(); ++i) {
- if (c == VIDEO && dynamic_cast<FixedFormat const *> (*i)) {
- _formats.push_back (*i);
- } else if (c == STILL && dynamic_cast<VariableFormat const *> (*i)) {
- _formats.push_back (*i);
+ for (unsigned int i = 0; i < paths.GetCount(); ++i) {
+ boost::filesystem::path p (wx_to_std (paths[i]));
+
+ shared_ptr<Content> c;
+
+ if (valid_image_file (p)) {
+ c.reset (new StillImageContent (_film, p));
+ } else if (SndfileContent::valid_file (p)) {
+ c.reset (new SndfileContent (_film, p));
+ } else {
+ c.reset (new FFmpegContent (_film, p));
}
- }
- _format->Clear ();
- for (vector<Format const *>::iterator i = _formats.begin(); i != _formats.end(); ++i) {
- _format->Append (std_to_wx ((*i)->name ()));
+ _film->examine_and_add_content (c);
}
-
- _film_sizer->Layout ();
}
void
- FilmEditor::with_subtitles_toggled (wxCommandEvent &)
+ FilmEditor::content_add_folder_clicked ()
{
- if (!_film) {
+ 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;
}
- _film->set_with_subtitles (_with_subtitles->GetValue ());
+ _film->examine_and_add_content (
+ shared_ptr<MovingImageContent> (
+ new MovingImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ())))
+ )
+ );
}
void
- FilmEditor::setup_subtitle_control_sensitivity ()
+ FilmEditor::content_remove_clicked ()
{
- bool h = false;
- if (_generally_sensitive && _film) {
- h = !_film->subtitle_streams().empty();
+ shared_ptr<Content> c = selected_content ();
+ if (c) {
+ _film->remove_content (c);
}
-
- _with_subtitles->Enable (h);
- bool j = false;
- if (_film) {
- j = _film->with_subtitles ();
- }
-
- _subtitle_stream->Enable (j);
- _subtitle_offset->Enable (j);
- _subtitle_scale->Enable (j);
+ content_selection_changed ();
}
void
- FilmEditor::setup_audio_control_sensitivity ()
+ FilmEditor::content_selection_changed ()
{
- _use_content_audio->Enable (_generally_sensitive);
- _use_external_audio->Enable (_generally_sensitive);
-
- bool const source = _generally_sensitive && _use_content_audio->GetValue();
- bool const external = _generally_sensitive && _use_external_audio->GetValue();
+ setup_content_sensitivity ();
+ shared_ptr<Content> s = selected_content ();
- _audio_stream->Enable (source);
- for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
- _external_audio[i]->Enable (external);
- }
+ /* All other sensitivity in content panels should be triggered by
+ one of these.
+ */
+ film_content_changed (s, ContentProperty::POSITION);
+ film_content_changed (s, ContentProperty::LENGTH);
+ film_content_changed (s, ContentProperty::TRIM_START);
+ film_content_changed (s, ContentProperty::TRIM_END);
+ film_content_changed (s, VideoContentProperty::VIDEO_CROP);
+ film_content_changed (s, VideoContentProperty::VIDEO_RATIO);
+ film_content_changed (s, VideoContentProperty::VIDEO_FRAME_TYPE);
+ film_content_changed (s, VideoContentProperty::COLOUR_CONVERSION);
+ film_content_changed (s, AudioContentProperty::AUDIO_GAIN);
+ film_content_changed (s, AudioContentProperty::AUDIO_DELAY);
+ film_content_changed (s, AudioContentProperty::AUDIO_MAPPING);
+ film_content_changed (s, FFmpegContentProperty::AUDIO_STREAM);
+ film_content_changed (s, FFmpegContentProperty::AUDIO_STREAMS);
+ film_content_changed (s, FFmpegContentProperty::SUBTITLE_STREAM);
+ film_content_changed (s, FFmpegContentProperty::SUBTITLE_STREAMS);
+ film_content_changed (s, FFmpegContentProperty::FILTERS);
+ film_content_changed (s, SubtitleContentProperty::SUBTITLE_OFFSET);
+ film_content_changed (s, SubtitleContentProperty::SUBTITLE_SCALE);
}
+ /** Set up broad sensitivity based on the type of content that is selected */
void
- FilmEditor::use_dci_name_toggled (wxCommandEvent &)
+ FilmEditor::setup_content_sensitivity ()
{
- if (!_film) {
- return;
- }
+ _content_add_file->Enable (_generally_sensitive);
+ _content_add_folder->Enable (_generally_sensitive);
- _film->set_use_dci_name (_use_dci_name->GetValue ());
+ shared_ptr<Content> selection = selected_content ();
+
+ _content_remove->Enable (selection && _generally_sensitive);
+ _content_timeline->Enable (_generally_sensitive);
+
+ _video_panel->Enable (selection && dynamic_pointer_cast<VideoContent> (selection) && _generally_sensitive);
+ _audio_panel->Enable (selection && dynamic_pointer_cast<AudioContent> (selection) && _generally_sensitive);
+ _subtitle_panel->Enable (selection && dynamic_pointer_cast<FFmpegContent> (selection) && _generally_sensitive);
+ _timing_panel->Enable (selection && _generally_sensitive);
}
- void
- FilmEditor::edit_dci_button_clicked (wxCommandEvent &)
+ shared_ptr<Content>
+ FilmEditor::selected_content ()
{
- if (!_film) {
- return;
+ int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ if (s == -1) {
+ return shared_ptr<Content> ();
}
- DCINameDialog* d = new DCINameDialog (this, _film);
- d->ShowModal ();
- d->Destroy ();
+ ContentList c = _film->content ();
+ if (s < 0 || size_t (s) >= c.size ()) {
+ return shared_ptr<Content> ();
+ }
+
+ return c[s];
}
- void
- FilmEditor::setup_streams ()
+ shared_ptr<VideoContent>
+ FilmEditor::selected_video_content ()
{
- _audio_stream->Clear ();
- vector<shared_ptr<AudioStream> > a = _film->content_audio_streams ();
- for (vector<shared_ptr<AudioStream> >::iterator i = a.begin(); i != a.end(); ++i) {
- shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (*i);
- assert (ffa);
- _audio_stream->Append (std_to_wx (ffa->name()), new wxStringClientData (std_to_wx (ffa->to_string ())));
- }
-
- if (_film->use_content_audio() && _film->audio_stream()) {
- checked_set (_audio_stream, _film->audio_stream()->to_string());
+ shared_ptr<Content> c = selected_content ();
+ if (!c) {
+ return shared_ptr<VideoContent> ();
}
- _subtitle_stream->Clear ();
- vector<shared_ptr<SubtitleStream> > s = _film->subtitle_streams ();
- for (vector<shared_ptr<SubtitleStream> >::iterator i = s.begin(); i != s.end(); ++i) {
- _subtitle_stream->Append (std_to_wx ((*i)->name()), new wxStringClientData (std_to_wx ((*i)->to_string ())));
- }
- if (_film->subtitle_stream()) {
- checked_set (_subtitle_stream, _film->subtitle_stream()->to_string());
- } else {
- _subtitle_stream->SetValue (wxT (""));
+ return dynamic_pointer_cast<VideoContent> (c);
+ }
+
+ shared_ptr<AudioContent>
+ FilmEditor::selected_audio_content ()
+ {
+ shared_ptr<Content> c = selected_content ();
+ if (!c) {
+ return shared_ptr<AudioContent> ();
}
+
+ return dynamic_pointer_cast<AudioContent> (c);
}
- void
- FilmEditor::audio_stream_changed (wxCommandEvent &)
+ shared_ptr<SubtitleContent>
+ FilmEditor::selected_subtitle_content ()
{
- if (!_film) {
- return;
+ shared_ptr<Content> c = selected_content ();
+ if (!c) {
+ return shared_ptr<SubtitleContent> ();
}
- _film->set_content_audio_stream (
- audio_stream_factory (
- string_client_data (_audio_stream->GetClientObject (_audio_stream->GetSelection ())),
- Film::state_version
- )
- );
+ return dynamic_pointer_cast<SubtitleContent> (c);
}
void
- FilmEditor::subtitle_stream_changed (wxCommandEvent &)
+ FilmEditor::content_timeline_clicked ()
{
- if (!_film) {
- return;
+ if (_timeline_dialog) {
+ _timeline_dialog->Destroy ();
+ _timeline_dialog = 0;
}
-
- _film->set_subtitle_stream (
- subtitle_stream_factory (
- string_client_data (_subtitle_stream->GetClientObject (_subtitle_stream->GetSelection ())),
- Film::state_version
- )
- );
+
+ _timeline_dialog = new TimelineDialog (this, _film);
+ _timeline_dialog->Show ();
}
void
- FilmEditor::setup_audio_details ()
+ FilmEditor::set_selection (weak_ptr<Content> wc)
{
- if (!_film->audio_stream()) {
- _audio->SetLabel (wxT (""));
- } else {
- stringstream s;
- if (_film->audio_stream()->channels() == 1) {
- s << "1 channel";
+ 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 {
- s << _film->audio_stream()->channels () << " channels";
+ _content->SetItemState (i, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
}
- s << ", " << _film->audio_stream()->sample_rate() << "Hz";
- _audio->SetLabel (std_to_wx (s.str ()));
}
}
void
- FilmEditor::active_jobs_changed (bool a)
+ FilmEditor::sequence_video_changed ()
{
- set_things_sensitive (!a);
+ if (!_film) {
+ return;
+ }
+
+ _film->set_sequence_video (_sequence_video->GetValue ());
}
void
- FilmEditor::use_audio_changed (wxCommandEvent &)
+ FilmEditor::content_right_click (wxListEvent& ev)
{
- _film->set_use_content_audio (_use_content_audio->GetValue());
+ ContentList cl;
+ cl.push_back (selected_content ());
+ _menu.popup (cl, ev.GetPoint ());
}
void
- FilmEditor::external_audio_changed (wxCommandEvent &)
+ FilmEditor::three_d_changed ()
{
- vector<string> a;
- for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
- a.push_back (wx_to_std (_external_audio[i]->GetPath()));
+ if (!_film) {
+ return;
}
- _film->set_external_audio (a);
- }
-
- void
- FilmEditor::setup_reel_control_sensitivity ()
- {
- _reel_size->Enable (_multiple_reels->GetValue ());
+ _film->set_three_d (_three_d->GetValue ());
}
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2013 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
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-
+
/** @file src/film_editor.h
* @brief A wx widget to edit a film's metadata, and perform various functions.
*/
#include <wx/collpane.h>
#include <boost/signals2.hpp>
#include "lib/film.h"
+ #include "content_menu.h"
class wxNotebook;
-
+ class wxListCtrl;
+ class wxListEvent;
class Film;
+ class TimelineDialog;
+ class Ratio;
+ class Timecode;
+ class FilmEditorPanel;
+ class SubtitleContent;
/** @class FilmEditor
* @brief A wx widget to edit a film's metadata, and perform various functions.
FilmEditor (boost::shared_ptr<Film>, wxWindow *);
void set_film (boost::shared_ptr<Film>);
- void setup_visibility ();
+ void set_selection (boost::weak_ptr<Content>);
boost::signals2::signal<void (std::string)> FileChanged;
+ /* Stuff for panels */
+
+ wxNotebook* content_notebook () const {
+ return _content_notebook;
+ }
+
+ boost::shared_ptr<Film> film () const {
+ return _film;
+ }
+
+ boost::shared_ptr<Content> selected_content ();
+ boost::shared_ptr<VideoContent> selected_video_content ();
+ boost::shared_ptr<AudioContent> selected_audio_content ();
+ boost::shared_ptr<SubtitleContent> selected_subtitle_content ();
+
private:
- void make_film_panel ();
- void make_video_panel ();
- void make_audio_panel ();
- void make_subtitle_panel ();
+ void make_dcp_panel ();
+ void make_content_panel ();
void connect_to_widgets ();
/* Handle changes to the view */
- void name_changed (wxCommandEvent &);
- void use_dci_name_toggled (wxCommandEvent &);
- void edit_dci_button_clicked (wxCommandEvent &);
- void left_crop_changed (wxCommandEvent &);
- void right_crop_changed (wxCommandEvent &);
- void top_crop_changed (wxCommandEvent &);
- void bottom_crop_changed (wxCommandEvent &);
- void content_changed (wxCommandEvent &);
- void trust_content_header_changed (wxCommandEvent &);
- void format_changed (wxCommandEvent &);
- void dcp_trim_start_changed (wxCommandEvent &);
- void dcp_trim_end_changed (wxCommandEvent &);
- void multiple_reels_toggled (wxCommandEvent &);
- void reel_size_changed (wxCommandEvent &);
- void dcp_content_type_changed (wxCommandEvent &);
- void encrypted_toggled (wxCommandEvent &);
- void dcp_ab_toggled (wxCommandEvent &);
- void scaler_changed (wxCommandEvent &);
- void audio_gain_changed (wxCommandEvent &);
- void audio_gain_calculate_button_clicked (wxCommandEvent &);
- void audio_delay_changed (wxCommandEvent &);
- void with_subtitles_toggled (wxCommandEvent &);
- void subtitle_offset_changed (wxCommandEvent &);
- void subtitle_scale_changed (wxCommandEvent &);
- void colour_lut_changed (wxCommandEvent &);
- void j2k_bandwidth_changed (wxCommandEvent &);
- void still_duration_changed (wxCommandEvent &);
- void audio_stream_changed (wxCommandEvent &);
- void subtitle_stream_changed (wxCommandEvent &);
- void use_audio_changed (wxCommandEvent &);
- void external_audio_changed (wxCommandEvent &);
+ void name_changed ();
+ void use_dci_name_toggled ();
+ void edit_dci_button_clicked ();
+ void content_selection_changed ();
+ void content_add_file_clicked ();
+ void content_add_folder_clicked ();
+ void content_remove_clicked ();
+ void container_changed ();
+ void dcp_content_type_changed ();
+ void scaler_changed ();
+ void j2k_bandwidth_changed ();
+ void frame_rate_changed ();
+ void best_frame_rate_clicked ();
+ void content_timeline_clicked ();
+ void audio_channels_changed ();
+ void resolution_changed ();
+ void sequence_video_changed ();
+ void content_right_click (wxListEvent &);
+ void three_d_changed ();
+ void standard_changed ();
++ void encrypted_toggled ();
/* Handle changes to the model */
void film_changed (Film::Property);
+ void film_content_changed (boost::weak_ptr<Content>, int);
- /* Button clicks */
- void edit_filters_clicked (wxCommandEvent &);
-
- void set_things_sensitive (bool);
- void setup_formats ();
- void setup_subtitle_control_sensitivity ();
- void setup_audio_control_sensitivity ();
- void setup_reel_control_sensitivity ();
- void setup_streams ();
- void setup_audio_details ();
+ void set_general_sensitivity (bool);
+ void setup_dcp_name ();
+ void setup_content ();
+ void setup_container ();
+ void setup_content_sensitivity ();
- wxControl* video_control (wxControl *);
- wxControl* still_control (wxControl *);
-
void active_jobs_changed (bool);
- wxNotebook* _notebook;
- wxPanel* _film_panel;
- wxSizer* _film_sizer;
- wxPanel* _video_panel;
- wxSizer* _video_sizer;
- wxPanel* _audio_panel;
- wxSizer* _audio_sizer;
- wxPanel* _subtitle_panel;
- wxSizer* _subtitle_sizer;
+ FilmEditorPanel* _video_panel;
+ FilmEditorPanel* _audio_panel;
+ FilmEditorPanel* _subtitle_panel;
+ FilmEditorPanel* _timing_panel;
+ std::list<FilmEditorPanel *> _panels;
+
+ wxNotebook* _main_notebook;
+ wxNotebook* _content_notebook;
+ wxPanel* _dcp_panel;
+ wxSizer* _dcp_sizer;
+ wxPanel* _content_panel;
+ wxSizer* _content_sizer;
/** The film we are editing */
boost::shared_ptr<Film> _film;
- /** The Film's name */
wxTextCtrl* _name;
wxStaticText* _dcp_name;
wxCheckBox* _use_dci_name;
+ wxChoice* _container;
+ wxListCtrl* _content;
+ wxButton* _content_add_file;
+ wxButton* _content_add_folder;
+ wxButton* _content_remove;
+ wxButton* _content_earlier;
+ wxButton* _content_later;
+ wxButton* _content_timeline;
+ wxCheckBox* _sequence_video;
wxButton* _edit_dci_button;
- /** The Film's format */
- wxComboBox* _format;
- /** The Film's content file */
- wxFilePickerCtrl* _content;
- wxCheckBox* _trust_content_header;
- /** The Film's left crop */
- wxSpinCtrl* _left_crop;
- /** The Film's right crop */
- wxSpinCtrl* _right_crop;
- /** The Film's top crop */
- wxSpinCtrl* _top_crop;
- /** The Film's bottom crop */
- wxSpinCtrl* _bottom_crop;
- /** Currently-applied filters */
- wxStaticText* _filters;
- /** Button to open the filters dialogue */
- wxButton* _filters_button;
- /** The Film's scaler */
- wxComboBox* _scaler;
- wxRadioButton* _use_content_audio;
- wxComboBox* _audio_stream;
- wxRadioButton* _use_external_audio;
- wxFilePickerCtrl* _external_audio[MAX_AUDIO_CHANNELS];
- /** The Film's audio gain */
- wxSpinCtrl* _audio_gain;
- /** A button to open the gain calculation dialogue */
- wxButton* _audio_gain_calculate_button;
- /** The Film's audio delay */
- wxSpinCtrl* _audio_delay;
- wxCheckBox* _with_subtitles;
- wxComboBox* _subtitle_stream;
- wxSpinCtrl* _subtitle_offset;
- wxSpinCtrl* _subtitle_scale;
- wxComboBox* _colour_lut;
- wxSpinCtrl* _j2k_bandwidth;
- /** The Film's DCP content type */
- wxComboBox* _dcp_content_type;
- /** The Film's frames per second */
- wxStaticText* _frames_per_second;
- /** The Film's original size */
- wxStaticText* _original_size;
- /** The Film's length */
- wxStaticText* _length;
- /** The Film's audio details */
- wxStaticText* _audio;
- /** The Film's duration for still sources */
- wxSpinCtrl* _still_duration;
-
- wxSpinCtrl* _dcp_trim_start;
- wxSpinCtrl* _dcp_trim_end;
+ wxChoice* _scaler;
+ wxSpinCtrl* _j2k_bandwidth;
+ wxChoice* _dcp_content_type;
+ wxChoice* _frame_rate;
+ wxSpinCtrl* _audio_channels;
+ wxButton* _best_frame_rate;
+ wxCheckBox* _three_d;
+ wxChoice* _resolution;
+ wxChoice* _standard;
+ wxCheckBox* _encrypted;
- wxCheckBox* _multiple_reels;
- wxSpinCtrl* _reel_size;
- /** Selector to generate an A/B comparison DCP */
- wxCheckBox* _dcp_ab;
- std::list<wxControl*> _video_controls;
- std::list<wxControl*> _still_controls;
+ ContentMenu _menu;
- std::vector<Format const *> _formats;
+ std::vector<Ratio const *> _ratios;
bool _generally_sensitive;
+ TimelineDialog* _timeline_dialog;
};
--- /dev/null
-
- add_label_to_sizer (vertical, this, "Make KDMs for");
-
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/treectrl.h>
+#include <wx/datectrl.h>
+#include <wx/timectrl.h>
+#include "lib/cinema.h"
+#include "lib/config.h"
+#include "kdm_dialog.h"
+#include "cinema_dialog.h"
+#include "screen_dialog.h"
+#include "wx_util.h"
+#ifdef __WXMSW__
+#include "dir_picker_ctrl.h"
+#else
+#include <wx/filepicker.h>
+#endif
+
+using std::string;
+using std::map;
+using std::list;
+using std::pair;
+using std::make_pair;
+using boost::shared_ptr;
+
+KDMDialog::KDMDialog (wxWindow* parent)
+ : wxDialog (parent, wxID_ANY, _("Make KDMs"))
+{
+ wxBoxSizer* vertical = new wxBoxSizer (wxVERTICAL);
- target_buttons->Add (_add_cinema, 1, 0, 6);
+ wxBoxSizer* targets = new wxBoxSizer (wxHORIZONTAL);
+
+ _targets = new wxTreeCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_HIDE_ROOT | wxTR_MULTIPLE | wxTR_HAS_BUTTONS);
+ targets->Add (_targets, 1, wxEXPAND | wxALL, 6);
+
+ _root = _targets->AddRoot ("Foo");
+
+ list<shared_ptr<Cinema> > c = Config::instance()->cinemas ();
+ for (list<shared_ptr<Cinema> >::iterator i = c.begin(); i != c.end(); ++i) {
+ add_cinema (*i);
+ }
+
+ _targets->ExpandAll ();
+
+ wxBoxSizer* target_buttons = new wxBoxSizer (wxVERTICAL);
+
+ _add_cinema = new wxButton (this, wxID_ANY, _("Add Cinema..."));
- target_buttons->Add (_edit_cinema, 1, 0, 6);
++ target_buttons->Add (_add_cinema, 1, wxEXPAND, 6);
+ _edit_cinema = new wxButton (this, wxID_ANY, _("Edit Cinema..."));
- target_buttons->Add (_remove_cinema, 1, 0, 6);
++ target_buttons->Add (_edit_cinema, 1, wxEXPAND, 6);
+ _remove_cinema = new wxButton (this, wxID_ANY, _("Remove Cinema"));
- target_buttons->Add (_add_screen, 1, 0, 6);
++ target_buttons->Add (_remove_cinema, 1, wxEXPAND, 6);
+
+ _add_screen = new wxButton (this, wxID_ANY, _("Add Screen..."));
- target_buttons->Add (_edit_screen, 1, 0, 6);
++ target_buttons->Add (_add_screen, 1, wxEXPAND, 6);
+ _edit_screen = new wxButton (this, wxID_ANY, _("Edit Screen..."));
- target_buttons->Add (_remove_screen, 1, 0, 6);
++ target_buttons->Add (_edit_screen, 1, wxEXPAND, 6);
+ _remove_screen = new wxButton (this, wxID_ANY, _("Remove Screen"));
- add_label_to_sizer (table, this, "From");
++ target_buttons->Add (_remove_screen, 1, wxEXPAND, 6);
+
+ targets->Add (target_buttons, 0, 0, 6);
+
+ vertical->Add (targets, 1, wxEXPAND | wxALL, 6);
+
+ wxFlexGridSizer* table = new wxFlexGridSizer (3, 2, 6);
- add_label_to_sizer (table, this, "Until");
++ add_label_to_sizer (table, this, "From", true);
+ _from_date = new wxDatePickerCtrl (this, wxID_ANY);
+ table->Add (_from_date, 1, wxEXPAND);
+ _from_time = new wxTimePickerCtrl (this, wxID_ANY);
+ table->Add (_from_time, 1, wxEXPAND);
+
- add_label_to_sizer (table, this, "Write to");
++ add_label_to_sizer (table, this, "Until", true);
+ _until_date = new wxDatePickerCtrl (this, wxID_ANY);
+ table->Add (_until_date, 1, wxEXPAND);
+ _until_time = new wxTimePickerCtrl (this, wxID_ANY);
+ table->Add (_until_time, 1, wxEXPAND);
+
- wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
++ add_label_to_sizer (table, this, "Write to", true);
+
+#ifdef __WXMSW__
+ _folder = new DirPickerCtrl (this);
+#else
+ _folder = new wxDirPickerCtrl (this, wxDD_DIR_MUST_EXIST);
+#endif
+
+ table->Add (_folder, 1, wxEXPAND);
+
+ vertical->Add (table, 0, wxEXPAND | wxALL, 6);
+
++ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+ if (buttons) {
+ vertical->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ _targets->Connect (wxID_ANY, wxEVT_COMMAND_TREE_SEL_CHANGED, wxCommandEventHandler (KDMDialog::targets_selection_changed), 0, this);
+
+ _add_cinema->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::add_cinema_clicked), 0, this);
+ _edit_cinema->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::edit_cinema_clicked), 0, this);
+ _remove_cinema->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::remove_cinema_clicked), 0, this);
+
+ _add_screen->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::add_screen_clicked), 0, this);
+ _edit_screen->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::edit_screen_clicked), 0, this);
+ _remove_screen->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::remove_screen_clicked), 0, this);
+
+ setup_sensitivity ();
+
+ SetSizer (vertical);
+ vertical->Layout ();
+ vertical->SetSizeHints (this);
+}
+
+list<pair<wxTreeItemId, shared_ptr<Cinema> > >
+KDMDialog::selected_cinemas () const
+{
+ wxArrayTreeItemIds s;
+ _targets->GetSelections (s);
+
+ list<pair<wxTreeItemId, shared_ptr<Cinema> > > c;
+ for (size_t i = 0; i < s.GetCount(); ++i) {
+ map<wxTreeItemId, shared_ptr<Cinema> >::const_iterator j = _cinemas.find (s[i]);
+ if (j != _cinemas.end ()) {
+ c.push_back (make_pair (j->first, j->second));
+ }
+ }
+
+ return c;
+}
+
+list<pair<wxTreeItemId, shared_ptr<Screen> > >
+KDMDialog::selected_screens () const
+{
+ wxArrayTreeItemIds s;
+ _targets->GetSelections (s);
+
+ list<pair<wxTreeItemId, shared_ptr<Screen> > > c;
+ for (size_t i = 0; i < s.GetCount(); ++i) {
+ map<wxTreeItemId, shared_ptr<Screen> >::const_iterator j = _screens.find (s[i]);
+ if (j != _screens.end ()) {
+ c.push_back (make_pair (j->first, j->second));
+ }
+ }
+
+ return c;
+}
+
+void
+KDMDialog::targets_selection_changed (wxCommandEvent &)
+{
+ setup_sensitivity ();
+}
+
+void
+KDMDialog::setup_sensitivity ()
+{
+ bool const sc = selected_cinemas().size() == 1;
+ bool const ss = selected_screens().size() == 1;
+
+ _edit_cinema->Enable (sc);
+ _remove_cinema->Enable (sc);
+
+ _add_screen->Enable (sc);
+ _edit_screen->Enable (ss);
+ _remove_screen->Enable (ss);
+}
+
+void
+KDMDialog::add_cinema (shared_ptr<Cinema> c)
+{
+ _cinemas[_targets->AppendItem (_root, std_to_wx (c->name))] = c;
+
+ for (list<shared_ptr<Screen> >::iterator i = c->screens.begin(); i != c->screens.end(); ++i) {
+ add_screen (c, *i);
+ }
+}
+
+void
+KDMDialog::add_screen (shared_ptr<Cinema> c, shared_ptr<Screen> s)
+{
+ map<wxTreeItemId, shared_ptr<Cinema> >::const_iterator i = _cinemas.begin();
+ while (i != _cinemas.end() && i->second != c) {
+ ++i;
+ }
+
+ if (i == _cinemas.end()) {
+ return;
+ }
+
+ _screens[_targets->AppendItem (i->first, std_to_wx (s->name))] = s;
+}
+
+void
+KDMDialog::add_cinema_clicked (wxCommandEvent &)
+{
+ CinemaDialog* d = new CinemaDialog (this, "Add Cinema");
+ d->ShowModal ();
+
+ shared_ptr<Cinema> c (new Cinema (d->name(), d->email()));
+ Config::instance()->add_cinema (c);
+ add_cinema (c);
+
+ Config::instance()->write ();
+
+ d->Destroy ();
+}
+
+void
+KDMDialog::edit_cinema_clicked (wxCommandEvent &)
+{
+ if (selected_cinemas().size() != 1) {
+ return;
+ }
+
+ pair<wxTreeItemId, shared_ptr<Cinema> > c = selected_cinemas().front();
+
+ CinemaDialog* d = new CinemaDialog (this, "Edit cinema", c.second->name, c.second->email);
+ d->ShowModal ();
+
+ c.second->name = d->name ();
+ c.second->email = d->email ();
+ _targets->SetItemText (c.first, std_to_wx (d->name()));
+
+ Config::instance()->write ();
+
+ d->Destroy ();
+}
+
+void
+KDMDialog::remove_cinema_clicked (wxCommandEvent &)
+{
+ if (selected_cinemas().size() != 1) {
+ return;
+ }
+
+ pair<wxTreeItemId, shared_ptr<Cinema> > c = selected_cinemas().front();
+
+ Config::instance()->remove_cinema (c.second);
+ _targets->Delete (c.first);
+
+ Config::instance()->write ();
+}
+
+void
+KDMDialog::add_screen_clicked (wxCommandEvent &)
+{
+ if (selected_cinemas().size() != 1) {
+ return;
+ }
+
+ shared_ptr<Cinema> c = selected_cinemas().front().second;
+
+ ScreenDialog* d = new ScreenDialog (this, "Add Screen");
+ d->ShowModal ();
+
+ shared_ptr<Screen> s (new Screen (d->name(), d->certificate()));
+ c->screens.push_back (s);
+ add_screen (c, s);
+
+ Config::instance()->write ();
+
+ d->Destroy ();
+}
+
+void
+KDMDialog::edit_screen_clicked (wxCommandEvent &)
+{
+ if (selected_screens().size() != 1) {
+ return;
+ }
+
+ pair<wxTreeItemId, shared_ptr<Screen> > s = selected_screens().front();
+
+ ScreenDialog* d = new ScreenDialog (this, "Edit screen", s.second->name, s.second->certificate);
+ d->ShowModal ();
+
+ s.second->name = d->name ();
+ s.second->certificate = d->certificate ();
+ _targets->SetItemText (s.first, std_to_wx (d->name()));
+
+ Config::instance()->write ();
+
+ d->Destroy ();
+}
+
+void
+KDMDialog::remove_screen_clicked (wxCommandEvent &)
+{
+ if (selected_screens().size() != 1) {
+ return;
+ }
+
+ pair<wxTreeItemId, shared_ptr<Screen> > s = selected_screens().front();
+
+ map<wxTreeItemId, shared_ptr<Cinema> >::iterator i = _cinemas.begin ();
+ while (i != _cinemas.end() && find (i->second->screens.begin(), i->second->screens.end(), s.second) == i->second->screens.end()) {
+ ++i;
+ }
+
+ if (i == _cinemas.end()) {
+ return;
+ }
+
+ i->second->screens.remove (s.second);
+ _targets->Delete (s.first);
+
+ Config::instance()->write ();
+}
+
+list<shared_ptr<Screen> >
+KDMDialog::screens () const
+{
+ list<shared_ptr<Screen> > s;
+
+ list<pair<wxTreeItemId, shared_ptr<Cinema> > > cinemas = selected_cinemas ();
+ for (list<pair<wxTreeItemId, shared_ptr<Cinema> > >::iterator i = cinemas.begin(); i != cinemas.end(); ++i) {
+ for (list<shared_ptr<Screen> >::iterator j = i->second->screens.begin(); j != i->second->screens.end(); ++j) {
+ s.push_back (*j);
+ }
+ }
+
+ list<pair<wxTreeItemId, shared_ptr<Screen> > > screens = selected_screens ();
+ for (list<pair<wxTreeItemId, shared_ptr<Screen> > >::iterator i = screens.begin(); i != screens.end(); ++i) {
+ s.push_back (i->second);
+ }
+
+ s.sort ();
+ s.unique ();
+
+ return s;
+}
+
+boost::posix_time::ptime
+KDMDialog::from () const
+{
+ return posix_time (_from_date, _from_time);
+}
+
+boost::posix_time::ptime
+KDMDialog::posix_time (wxDatePickerCtrl* date_picker, wxTimePickerCtrl* time_picker)
+{
+ wxDateTime const date = date_picker->GetValue ();
+ wxDateTime const time = time_picker->GetValue ();
+ return boost::posix_time::ptime (
+ boost::gregorian::date (date.GetYear(), date.GetMonth() + 1, date.GetDay()),
+ boost::posix_time::time_duration (time.GetHour(), time.GetMinute(), time.GetSecond())
+ );
+}
+
+boost::posix_time::ptime
+KDMDialog::until () const
+{
+ return posix_time (_until_date, _until_time);
+}
+
+string
+KDMDialog::directory () const
+{
+ return wx_to_std (_folder->GetPath ());
+}
--- /dev/null
- add_label_to_sizer (table, this, "Name");
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/filepicker.h>
+#include <wx/validate.h>
+#include <libdcp/exceptions.h>
+#include "lib/compose.hpp"
+#include "screen_dialog.h"
+#include "wx_util.h"
+
+using std::string;
+using std::cout;
+using boost::shared_ptr;
+
+ScreenDialog::ScreenDialog (wxWindow* parent, string title, string name, shared_ptr<libdcp::Certificate> certificate)
+ : wxDialog (parent, wxID_ANY, std_to_wx (title))
+ , _certificate (certificate)
+{
+ wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6);
+ table->AddGrowableCol (1, 1);
+
- add_label_to_sizer (table, this, "Certificate");
++ add_label_to_sizer (table, this, "Name", true);
+ _name = new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (320, -1));
+ table->Add (_name, 1, wxEXPAND);
+
++ add_label_to_sizer (table, this, "Certificate", true);
+ _certificate_load = new wxButton (this, wxID_ANY, wxT ("Load from file..."));
+ table->Add (_certificate_load, 1, wxEXPAND);
+
+ table->AddSpacer (0);
+ _certificate_text = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, wxSize (320, 256), wxTE_MULTILINE | wxTE_READONLY);
+ if (certificate) {
+ _certificate_text->SetValue (certificate->certificate ());
+ }
+ wxFont font = wxSystemSettings::GetFont (wxSYS_ANSI_FIXED_FONT);
+ font.SetPointSize (font.GetPointSize() / 2);
+ _certificate_text->SetFont (font);
+ table->Add (_certificate_text, 1, wxEXPAND);
+
+ wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+ overall_sizer->Add (table, 1, wxEXPAND | wxALL, 6);
+
+ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+ if (buttons) {
+ overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ SetSizer (overall_sizer);
+ overall_sizer->Layout ();
+ overall_sizer->SetSizeHints (this);
+
+ _certificate_load->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (ScreenDialog::load_certificate), 0, this);
+}
+
+string
+ScreenDialog::name () const
+{
+ return wx_to_std (_name->GetValue());
+}
+
+shared_ptr<libdcp::Certificate>
+ScreenDialog::certificate () const
+{
+ return _certificate;
+}
+
+void
+ScreenDialog::load_certificate (wxCommandEvent &)
+{
+ wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
+ d->ShowModal ();
+
+ try {
+ _certificate.reset (new libdcp::Certificate (wx_to_std (d->GetPath ())));
+ _certificate_text->SetValue (_certificate->certificate ());
+ } catch (libdcp::MiscError& e) {
+ error_dialog (this, String::compose ("Could not read certificate file (%1)", e.what()));
+ }
+
+ d->Destroy ();
+}
+ import os
+ import glob
+ from waflib import Logs
+ import i18n
+
+ sources = """
+ about_dialog.cc
+ audio_dialog.cc
+ audio_mapping_view.cc
+ audio_panel.cc
+ audio_plot.cc
++ cinema_dialog.cc
+ colour_conversion_editor.cc
+ config_dialog.cc
+ content_colour_conversion_dialog.cc
+ content_menu.cc
+ dci_metadata_dialog.cc
+ dir_picker_ctrl.cc
+ film_editor.cc
+ film_editor_panel.cc
+ film_viewer.cc
+ filter_dialog.cc
+ filter_editor.cc
+ gain_calculator_dialog.cc
+ job_manager_view.cc
+ job_wrapper.cc
++ kdm_dialog.cc
+ new_film_dialog.cc
+ preset_colour_conversion_dialog.cc
+ properties_dialog.cc
+ repeat_dialog.cc
++ screen_dialog.cc
+ server_dialog.cc
+ subtitle_panel.cc
+ timecode.cc
+ timeline.cc
+ timeline_dialog.cc
+ timing_panel.cc
+ video_panel.cc
+ wx_util.cc
+ wx_ui_signaller.cc
+ """
+
def configure(conf):
- conf.check_cfg(package = '', path = 'wx-config', args = '--cppflags --cxxflags --libs', uselib_store = 'WXWIDGETS', mandatory = True)
+ args = '--cppflags --cxxflags'
+ if not conf.env.STATIC:
+ args += ' --libs'
+
+ conf.check_cfg(msg='Checking for wxWidgets', package='', path=conf.options.wx_config, args=args,
+ uselib_store='WXWIDGETS', mandatory=True)
+
+ if conf.env.STATIC:
+ # wx-config returns its static libraries as full paths, without -l prefixes, which confuses
+ # check_cfg(), so just hard-code it all.
+ conf.env.STLIB_WXWIDGETS = ['wx_gtk2u_xrc-2.9', 'wx_gtk2u_qa-2.9', 'wx_baseu_net-2.9', 'wx_gtk2u_html-2.9',
+ 'wx_gtk2u_adv-2.9', 'wx_gtk2u_core-2.9', 'wx_baseu_xml-2.9', 'wx_baseu-2.9']
+ conf.env.LIB_WXWIDGETS = ['tiff', 'SM', 'dl', 'jpeg', 'png', 'X11']
+
+ conf.in_msg = 1
+ wx_version = conf.check_cfg(package='', path=conf.options.wx_config, args='--version').strip()
+ conf.im_msg = 0
+ if wx_version != '2.9.4' and wx_version != '2.9.5':
+ conf.fatal('wxwidgets version 2.9.4 or 2.9.5 is required; %s found' % wx_version)
def build(bld):
if bld.env.STATIC:
else:
obj = bld(features = 'cxx cxxshlib')
- obj.name = 'libdvdomatic-wx'
- obj.includes = [ '..' ]
- obj.export_includes = ['.']
+ obj.name = 'libdcpomatic-wx'
+ # obj.includes = [ '..' ]
+ obj.export_includes = ['..']
obj.uselib = 'WXWIDGETS'
- obj.use = 'libdvdomatic'
- obj.source = """
- config_dialog.cc
- dci_name_dialog.cc
- dir_picker_ctrl.cc
- film_editor.cc
- film_viewer.cc
- filter_dialog.cc
- filter_view.cc
- gain_calculator_dialog.cc
- job_manager_view.cc
- job_wrapper.cc
- kdm_dialog.cc
- cinema_dialog.cc
- new_film_dialog.cc
- screen_dialog.cc
- properties_dialog.cc
- server_dialog.cc
- wx_util.cc
- wx_ui_signaller.cc
- """
-
- obj.target = 'dvdomatic-wx'
+ if bld.env.TARGET_LINUX:
+ obj.uselib += ' GTK'
+ obj.use = 'libdcpomatic'
+ obj.source = sources
+ obj.target = 'dcpomatic-wx'
+
+ i18n.po_to_mo(os.path.join('src', 'wx'), 'libdcpomatic-wx', bld)
+
+ def pot(bld):
+ i18n.pot(os.path.join('src', 'wx'), sources, 'libdcpomatic-wx')
+
+ def pot_merge(bld):
+ i18n.pot_merge(os.path.join('src', 'wx'), 'libdcpomatic-wx')