/*
- Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#include <dcp/local_time.h>
#include <dcp/decrypted_kdm.h>
#include <dcp/raw_convert.h>
+#include <dcp/reel_mxf.h>
+#include <dcp/reel_asset.h>
#include <libxml++/libxml++.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
using std::list;
using std::set;
using std::runtime_error;
+using std::copy;
+using std::back_inserter;
+using std::map;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::dynamic_pointer_cast;
, _resolution (RESOLUTION_2K)
, _signed (true)
, _encrypted (false)
+ , _context_id (dcp::make_uuid ())
, _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
, _isdcf_metadata (Config::instance()->default_isdcf_metadata ())
, _video_frame_rate (24)
throw BadSettingError (_("name"), _("cannot contain slashes"));
}
+ if (container() == 0) {
+ throw MissingSettingError (_("container"));
+ }
+
+ if (content().empty()) {
+ throw runtime_error (_("you must add some content to the DCP before creating it"));
+ }
+
+ if (dcp_content_type() == 0) {
+ throw MissingSettingError (_("content type"));
+ }
+
+ if (name().empty()) {
+ throw MissingSettingError (_("name"));
+ }
+
+ BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
+ if (!i->paths_valid()) {
+ throw runtime_error (_("some of your content is missing"));
+ }
+ shared_ptr<const DCPContent> dcp = dynamic_pointer_cast<const DCPContent> (i);
+ if (dcp && dcp->needs_kdm()) {
+ throw runtime_error (_("some of your content needs a KDM"));
+ }
+ if (dcp && dcp->needs_assets()) {
+ throw runtime_error (_("some of your content needs an OV"));
+ }
+ }
+
set_isdcf_date_today ();
BOOST_FOREACH (string i, environment_info ()) {
if (Config::instance()->only_servers_encode ()) {
LOG_GENERAL_NC ("0 threads: ONLY SERVERS SET TO ENCODE");
} else {
- LOG_GENERAL ("%1 threads", Config::instance()->num_local_encoding_threads());
+ LOG_GENERAL ("%1 threads", Config::instance()->master_encoding_threads());
}
LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
- if (container() == 0) {
- throw MissingSettingError (_("container"));
- }
-
- if (content().empty()) {
- throw runtime_error (_("You must add some content to the DCP before creating it"));
- }
-
- if (dcp_content_type() == 0) {
- throw MissingSettingError (_("content type"));
- }
-
- if (name().empty()) {
- throw MissingSettingError (_("name"));
- }
-
JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
}
root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
root->add_child("Key")->add_child_text (_key.hex ());
+ root->add_child("ContextID")->add_child_text (_context_id);
if (_audio_processor) {
root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
}
_three_d = f.bool_child ("ThreeD");
_interop = f.bool_child ("Interop");
_key = dcp::Key (f.string_child ("Key"));
+ _context_id = f.optional_string_child("ContextID").get_value_or (dcp::make_uuid ());
if (f.optional_string_child ("AudioProcessor")) {
_audio_processor = AudioProcessor::from_id (f.string_child ("AudioProcessor"));
}
/** 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.
+ * @param d directory name within the Filn's directory.
+ * @param create true to create the directory (and its parents) if they do not exist.
*/
boost::filesystem::path
-Film::dir (boost::filesystem::path d) const
+Film::dir (boost::filesystem::path d, bool create) const
{
DCPOMATIC_ASSERT (_directory);
p /= _directory.get();
p /= d;
- boost::filesystem::create_directories (p);
+ if (create) {
+ boost::filesystem::create_directories (p);
+ }
return p;
}
{
string unfiltered;
if (use_isdcf_name()) {
- unfiltered = isdcf_name (if_created_now);
- } else {
- unfiltered = name ();
+ return careful_string_filter (isdcf_name (if_created_now));
}
- /* Filter out `bad' characters which cause problems with some systems.
- There's no apparent list of what really is allowed, so this is a guess.
- */
-
- string filtered;
- string const allowed = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
- for (size_t i = 0; i < unfiltered.size(); ++i) {
- if (allowed.find (unfiltered[i]) != string::npos) {
- filtered += unfiltered[i];
- }
- }
-
- return filtered;
+ return careful_string_filter (name ());
}
void
return fit_ratio_within (container()->ratio(), full_frame ());
}
-/** @param from KDM from time expressed as a local time with an offset from UTC
- * @param to KDM to time expressed as a local time with an offset from UTC
+/** @param recipient KDM recipient certificate.
+ * @param trusted_devices Certificates of other trusted devices (can be empty).
+ * @param cpl_file CPL filename.
+ * @param from KDM from time expressed as a local time with an offset from UTC.
+ * @param until KDM to time expressed as a local time with an offset from UTC.
+ * @param formulation KDM formulation to use.
*/
dcp::EncryptedKDM
Film::make_kdm (
throw InvalidSignerError ();
}
+ /* Find keys that have been added to imported, encrypted DCP content */
+ list<dcp::DecryptedKDMKey> imported_keys;
+ BOOST_FOREACH (shared_ptr<Content> i, content()) {
+ shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
+ if (d && d->kdm()) {
+ dcp::DecryptedKDM kdm (d->kdm().get(), Config::instance()->decryption_chain()->key().get());
+ list<dcp::DecryptedKDMKey> keys = kdm.keys ();
+ copy (keys.begin(), keys.end(), back_inserter (imported_keys));
+ }
+ }
+
+ map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
+
+ BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
+ shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
+ if (!mxf || !mxf->key_id()) {
+ continue;
+ }
+
+ /* Get any imported key for this ID */
+ bool done = false;
+ BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
+ if (j.id() == mxf->key_id().get()) {
+ LOG_GENERAL ("Using imported key for %1", mxf->key_id().get());
+ keys[mxf] = j.key();
+ done = true;
+ }
+ }
+
+ if (!done) {
+ /* No imported key; it must be an asset that we encrypted */
+ LOG_GENERAL ("Using our own key for %1", mxf->key_id().get());
+ keys[mxf] = key();
+ }
+ }
+
return dcp::DecryptedKDM (
- cpl, key(), from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
+ cpl->id(), keys, from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
).encrypt (signer, recipient, trusted_devices, formulation);
}
-/** @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
- * @param to KDM to time expressed as a local time in the time zone of the Screen's Cinema.
+/** @param screens Screens to make KDMs for.
+ * @param cpl_file Path to CPL to make KDMs for.
+ * @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
+ * @param until KDM to time expressed as a local time in the time zone of the Screen's Cinema.
+ * @param formulation KDM formulation to use.
*/
list<ScreenKDM>
Film::make_kdms (
list<shared_ptr<Screen> > screens,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl_file,
boost::posix_time::ptime from,
boost::posix_time::ptime until,
dcp::Formulation formulation
dcp::EncryptedKDM const kdm = make_kdm (
i->recipient.get(),
i->trusted_devices,
- dcp,
+ cpl_file,
dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
formulation
Film::reels () const
{
list<DCPTimePeriod> p;
- DCPTime const len = length().round_up (video_frame_rate ());
+ DCPTime const len = length().ceil (video_frame_rate ());
switch (reel_type ()) {
case REELTYPE_SINGLE:
return p;
}
+/** @param period A period within the DCP
+ * @return Name of the content which most contributes to the given period.
+ */
string
Film::content_summary (DCPTimePeriod period) const
{