43 #include "compose.hpp"
50 #include "filesystem.h"
67 LIBDCP_DISABLE_WARNINGS
68 #include <asdcp/AS_DCP.h>
69 LIBDCP_ENABLE_WARNINGS
70 #include <xmlsec/xmldsig.h>
71 #include <xmlsec/app.h>
72 LIBDCP_DISABLE_WARNINGS
73 #include <libxml++/libxml++.h>
74 LIBDCP_ENABLE_WARNINGS
75 #include <boost/algorithm/string.hpp>
81 using std::dynamic_pointer_cast;
85 using std::make_shared;
87 using std::shared_ptr;
90 using boost::algorithm::starts_with;
91 using boost::optional;
95 static string const volindex_interop_ns =
"http://www.digicine.com/PROTO-ASDCP-VL-20040311#";
96 static string const volindex_smpte_ns =
"http://www.smpte-ra.org/schemas/429-9/2007/AM";
100 : _directory (directory)
102 if (!filesystem::exists(directory)) {
103 filesystem::create_directories(directory);
111 : _directory(std::move(other._directory))
112 , _cpls(std::move(other._cpls))
113 , _pkls(std::move(other._pkls))
114 , _asset_map(std::move(other._asset_map))
115 , _new_issuer(std::move(other._new_issuer))
116 , _new_creator(std::move(other._new_creator))
117 , _new_issue_date(std::move(other._new_issue_date))
118 , _new_annotation_text(std::move(other._new_annotation_text))
125 DCP::operator=(
DCP&& other)
128 _cpls = std::move(other._cpls);
129 _pkls = std::move(other._pkls);
130 _asset_map = std::move(other._asset_map);
131 _new_issuer = std::move(other._new_issuer);
132 _new_creator = std::move(other._new_creator);
133 _new_issue_date = std::move(other._new_issue_date);
134 _new_annotation_text = std::move(other._new_annotation_text);
140 DCP::read (vector<dcp::VerificationNote>* notes,
bool ignore_incorrect_picture_mxf_type)
144 boost::filesystem::path asset_map_path;
145 if (filesystem::exists(
_directory /
"ASSETMAP")) {
147 }
else if (filesystem::exists(
_directory /
"ASSETMAP.xml")) {
153 _asset_map =
AssetMap(asset_map_path);
154 auto const pkl_paths = _asset_map->pkl_paths();
155 auto const standard = _asset_map->standard();
157 if (pkl_paths.empty()) {
158 boost::throw_exception (
XMLError (
"No packing lists found in asset map"));
161 for (
auto i: pkl_paths) {
162 _pkls.push_back(make_shared<PKL>(i, notes));
175 vector<shared_ptr<Asset>> other_assets;
177 auto ids_and_paths = _asset_map->asset_ids_and_paths();
178 for (
auto id_and_path: ids_and_paths) {
179 auto const id = id_and_path.first;
180 auto const path = id_and_path.second;
193 if (!filesystem::exists(path)) {
201 optional<string> pkl_type;
202 for (
auto j:
_pkls) {
203 pkl_type = j->type(
id);
216 auto remove_parameters = [](
string const& n) {
217 return n.substr(0, n.find(
";"));
221 pkl_type = pkl_type->substr(0, pkl_type->find(
";"));
224 pkl_type == remove_parameters(CPL::static_pkl_type(
standard)) ||
225 pkl_type == remove_parameters(InteropTextAsset::static_pkl_type(
standard))) {
226 auto p =
new xmlpp::DomParser;
228 p->parse_file(dcp::filesystem::fix_long_path(path).string());
229 }
catch (std::exception& e) {
231 throw ReadError(String::compose(
"XML error in %1", path.string()), e.what());
234 auto const root = p->get_document()->get_root_node()->get_name();
237 if (root ==
"CompositionPlaylist") {
238 auto cpl = make_shared<CPL>(path, notes);
239 if (cpl->standard() !=
standard && notes) {
242 _cpls.push_back (cpl);
243 }
else if (root ==
"DCSubtitle") {
244 if (
standard == Standard::SMPTE && notes) {
247 other_assets.push_back (make_shared<InteropTextAsset>(path));
250 *pkl_type == remove_parameters(J2KPictureAsset::static_pkl_type(
standard)) ||
251 *pkl_type == remove_parameters(MPEG2PictureAsset::static_pkl_type(
standard)) ||
252 *pkl_type == remove_parameters(SoundAsset::static_pkl_type(
standard)) ||
253 *pkl_type == remove_parameters(AtmosAsset::static_pkl_type(
standard)) ||
254 *pkl_type == remove_parameters(SMPTETextAsset::static_pkl_type(
standard))
257 bool found_threed_marked_as_twod =
false;
258 auto asset =
asset_factory(path, ignore_incorrect_picture_mxf_type, &found_threed_marked_as_twod);
259 if (asset->id() !=
id) {
262 other_assets.push_back(asset);
263 if (found_threed_marked_as_twod && notes) {
266 }
else if (*pkl_type == remove_parameters(FontAsset::static_pkl_type(
standard))) {
267 other_assets.push_back(make_shared<FontAsset>(
id, path));
268 }
else if (*pkl_type ==
"image/png") {
271 throw ReadError (String::compose(
"Unknown asset type %1 in PKL", *pkl_type));
282 auto hash_from_pkl = [
this](
string id) -> optional<string> {
283 for (
auto pkl:
_pkls) {
284 if (
auto pkl_hash = pkl->hash(
id)) {
292 auto hash_from_cpl_or_pkl = [
this, &hash_from_pkl](
string id) -> optional<string> {
293 for (
auto cpl: cpls()) {
294 for (
auto reel_file_asset: cpl->reel_file_assets()) {
295 if (reel_file_asset->asset_ref().id() ==
id && reel_file_asset->hash()) {
296 return reel_file_asset->hash();
301 return hash_from_pkl(
id);
304 for (
auto asset: other_assets) {
305 if (
auto hash = hash_from_cpl_or_pkl(asset->id())) {
306 asset->set_hash(*hash);
310 for (
auto cpl: cpls()) {
311 if (
auto hash = hash_from_pkl(cpl->id())) {
312 cpl->set_hash(*hash);
317 resolve_refs (other_assets);
321 for (
auto i: cpls()) {
322 for (
auto j: i->reel_file_assets()) {
323 if (!j->asset_ref().resolved() && ids_and_paths.find(j->asset_ref().id()) == ids_and_paths.end()) {
333 DCP::resolve_refs (vector<shared_ptr<Asset>> assets)
335 for (
auto i: cpls()) {
345 auto b = other.cpls ();
347 if (a.size() != b.size()) {
348 note (NoteType::ERROR, String::compose (
"CPL counts differ: %1 vs %2", a.size(), b.size()));
356 while (j != b.end() && !(*j)->equals (i, opt, note)) {
370 DCP::add (shared_ptr<CPL> cpl)
372 _cpls.push_back (cpl);
377 DCP::any_encrypted ()
const
379 for (
auto i: cpls()) {
380 if (i->any_encrypted()) {
390 DCP::all_encrypted ()
const
392 for (
auto i: cpls()) {
393 if (!i->all_encrypted()) {
405 auto keys = kdm.
keys();
406 for (
auto cpl: cpls()) {
407 if (std::any_of(keys.begin(), keys.end(), [cpl](
DecryptedKDMKey const& key) { return key.cpl_id() == cpl->id(); })) {
422 case Standard::INTEROP:
425 case Standard::SMPTE:
433 xmlpp::Element* root;
436 case Standard::INTEROP:
437 root = doc.create_root_node (
"VolumeIndex", volindex_interop_ns);
439 case Standard::SMPTE:
440 root = doc.create_root_node (
"VolumeIndex", volindex_smpte_ns);
446 cxml::add_text_child(root,
"Index",
"1");
447 doc.write_to_file_formatted(dcp::filesystem::fix_long_path(p).
string(),
"UTF-8");
455 throw MiscError (
"Cannot write DCP with no CPLs.");
460 [](Standard s, shared_ptr<CPL> c) {
461 if (s != c->standard()) {
462 throw MiscError (
"Cannot make DCP with mixed Interop and SMPTE CPLs.");
468 for (
auto i: cpls()) {
469 NameFormat::Map values;
471 i->write_xml(_directory / (name_format.get(values,
"_" + i->id() +
".xml")), signer, include_mca_subdescriptors);
478 _new_annotation_text.get_value_or(String::compose(
"Created by libdcp %1", dcp::version)),
479 _new_issue_date.get_value_or(
LocalTime().as_string()),
480 _new_issuer.get_value_or(String::compose(
"libdcp %1", dcp::version)),
481 _new_creator.get_value_or(String::compose(
"libdcp %1", dcp::version))
486 auto pkl = _pkls.front();
490 for (
auto asset: assets()) {
491 asset->add_to_pkl(pkl, _directory);
494 NameFormat::Map values;
496 auto pkl_path = _directory / name_format.get(values,
"_" + pkl->id() +
".xml");
497 pkl->write_xml (pkl_path, signer);
502 _new_annotation_text.get_value_or(String::compose(
"Created by libdcp %1", dcp::version)),
503 _new_issue_date.get_value_or(
LocalTime().as_string()),
504 _new_issuer.get_value_or(String::compose(
"libdcp %1", dcp::version)),
505 _new_creator.get_value_or(String::compose(
"libdcp %1", dcp::version))
510 _asset_map->clear_assets();
511 _asset_map->add_asset(pkl->id(), pkl_path,
true);
512 for (
auto asset: assets()) {
513 asset->add_to_assetmap(*_asset_map, _directory);
516 _asset_map->write_xml(
517 _directory / (standard == Standard::INTEROP ?
"ASSETMAP" :
"ASSETMAP.xml")
520 write_volindex (standard);
524 vector<shared_ptr<CPL>>
531 vector<shared_ptr<Asset>>
534 vector<shared_ptr<Asset>>
assets;
535 for (
auto i: cpls()) {
537 for (
auto j: i->reel_file_assets()) {
538 if (ignore_unresolved && !j->asset_ref().resolved()) {
542 auto const id = j->asset_ref().id();
543 if (std::find_if(
assets.begin(),
assets.end(), [
id](shared_ptr<Asset> asset) { return asset->id() == id; }) ==
assets.end()) {
544 auto o = j->asset_ref().asset();
547 auto sub = dynamic_pointer_cast<InteropTextAsset>(o);
549 add_to_container(
assets, sub->font_assets());
560 vector<boost::filesystem::path>
563 vector<boost::filesystem::path> d;
564 for (
auto i: files) {
565 if (i.filename() ==
"ASSETMAP" || i.filename() ==
"ASSETMAP.xml") {
566 d.push_back (i.parent_path ());
574 DCP::set_issuer(
string issuer)
576 for (
auto pkl:
_pkls) {
577 pkl->set_issuer(issuer);
580 _asset_map->set_issuer(issuer);
582 _new_issuer = issuer;
587 DCP::set_creator(
string creator)
589 for (
auto pkl:
_pkls) {
590 pkl->set_creator(creator);
593 _asset_map->set_creator(creator);
595 _new_creator = creator;
600 DCP::set_issue_date(
string issue_date)
602 for (
auto pkl:
_pkls) {
603 pkl->set_issue_date(issue_date);
606 _asset_map->set_issue_date(issue_date);
608 _new_issue_date = issue_date;
613 DCP::set_annotation_text(
string annotation_text)
615 for (
auto pkl:
_pkls) {
616 pkl->set_annotation_text(annotation_text);
619 _asset_map->set_annotation_text(annotation_text);
621 _new_annotation_text = annotation_text;
A class to create or read a DCP.
DCP(boost::filesystem::path directory)
static std::vector< boost::filesystem::path > directories_from_files(std::vector< boost::filesystem::path > files)
bool equals(DCP const &other, EqualityOptions const &options, NoteHandler note) const
std::vector< std::shared_ptr< Asset > > assets(bool ignore_unresolved=false) const
std::vector< std::shared_ptr< PKL > > _pkls
boost::filesystem::path _directory
std::vector< std::shared_ptr< CPL > > _cpls
void write_xml(std::shared_ptr< const CertificateChain > signer=std::shared_ptr< const CertificateChain >(), bool include_mca_subdescriptors=true, NameFormat name_format=NameFormat("%t"))
boost::optional< Standard > standard() const
void read(std::vector< VerificationNote > *notes=nullptr, bool ignore_incorrect_picture_mxf_type=false)
void write_volindex(Standard standard) const
An un- or de-crypted key from a KDM.
std::vector< DecryptedKDMKey > keys() const
A class to describe what "equality" means for a particular test.
A representation of a local time (down to the second), including its offset from GMT (equivalent to x...
A miscellaneous exception.
Thrown when no ASSETMAP was found when trying to read a DCP.
Any error that occurs when reading data from a DCP.
@ MISMATCHED_ASSET_MAP_ID
@ THREED_ASSET_MARKED_AS_TWOD
Exceptions thrown by libdcp.
MonoJ2KPictureAsset class.
MonoMPEG2PictureAsset class.
Namespace for everything in libdcp.
std::shared_ptr< Asset > asset_factory(boost::filesystem::path path, bool ignore_incorrect_picture_mxf_type, bool *found_threed_marked_as_twod=nullptr)
Methods for conversion to/from string.
StereoJ2KPictureAsset class.
Utility methods and classes.
dcp::verify() method and associated code