diff options
| author | Carl Hetherington <cth@carlh.net> | 2022-04-24 21:39:42 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2022-04-24 21:39:42 +0200 |
| commit | c1d782e29c4fd0ae6de7a3c5b0f47833580293bd (patch) | |
| tree | d90ff8554a2a04d60966ab928708187053610228 | |
| parent | 232e17ff2944d4e0dca311be9b1bc703055d71b4 (diff) | |
wip: add AssetMap and fix everything up.dcp-editor
| -rw-r--r-- | src/asset.cc | 24 | ||||
| -rw-r--r-- | src/asset.h | 7 | ||||
| -rw-r--r-- | src/asset_map.cc | 204 | ||||
| -rw-r--r-- | src/asset_map.h | 110 | ||||
| -rw-r--r-- | src/dcp.cc | 192 | ||||
| -rw-r--r-- | src/dcp.h | 19 | ||||
| -rw-r--r-- | src/interop_subtitle_asset.cc | 8 | ||||
| -rw-r--r-- | src/interop_subtitle_asset.h | 3 | ||||
| -rw-r--r-- | src/wscript | 1 | ||||
| -rw-r--r-- | test/util_test.cc | 38 |
10 files changed, 414 insertions, 192 deletions
diff --git a/src/asset.cc b/src/asset.cc index eb6f4a07..21282e9d 100644 --- a/src/asset.cc +++ b/src/asset.cc @@ -38,6 +38,7 @@ #include "asset.h" +#include "asset_map.h" #include "compose.hpp" #include "dcp_assert.h" #include "exceptions.h" @@ -98,37 +99,24 @@ Asset::add_to_pkl (shared_ptr<PKL> pkl, path root) const void -Asset::write_to_assetmap (xmlpp::Node* node, path root) const +Asset::add_to_assetmap (AssetMap& asset_map, path root) const { DCP_ASSERT (_file); - write_file_to_assetmap (node, root, _file.get(), _id); + add_file_to_assetmap (asset_map, root, _file.get(), _id); } void -Asset::write_file_to_assetmap (xmlpp::Node* node, path root, path file, string id) +Asset::add_file_to_assetmap (AssetMap& asset_map, path root, path file, string id) { - auto path = relative_to_root ( - canonical(root), - canonical(file) - ); - - if (!path) { + if (!relative_to_root(root, file)) { /* The path of this asset is not within our DCP, so we assume it's an external (referenced) one. */ return; } - auto asset = node->add_child ("Asset"); - asset->add_child("Id")->add_child_text("urn:uuid:" + id); - auto chunk_list = asset->add_child ("ChunkList"); - auto chunk = chunk_list->add_child ("Chunk"); - - chunk->add_child("Path")->add_child_text(path.get().generic_string()); - chunk->add_child("VolumeIndex")->add_child_text("1"); - chunk->add_child("Offset")->add_child_text("0"); - chunk->add_child("Length")->add_child_text(raw_convert<string>(file_size(file))); + asset_map.add_asset(id, file, false); } diff --git a/src/asset.h b/src/asset.h index 88c7d1d1..3af49119 100644 --- a/src/asset.h +++ b/src/asset.h @@ -60,6 +60,9 @@ struct asset_test; namespace dcp { +class AssetMap; + + /** @class Asset * @brief Parent class for DCP assets, i.e. picture, sound, subtitles, closed captions, CPLs, fonts * @@ -89,7 +92,7 @@ public: NoteHandler note ) const; - virtual void write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const; + virtual void add_to_assetmap (AssetMap& asset_map, boost::filesystem::path root) const; virtual void add_to_pkl (std::shared_ptr<PKL> pkl, boost::filesystem::path root) const; @@ -121,7 +124,7 @@ protected: /** The most recent disk file used to read or write this asset */ mutable boost::optional<boost::filesystem::path> _file; - static void write_file_to_assetmap (xmlpp::Node* node, boost::filesystem::path root, boost::filesystem::path file, std::string id); + static void add_file_to_assetmap (AssetMap& asset_map, boost::filesystem::path root, boost::filesystem::path file, std::string id); private: friend struct ::asset_test; diff --git a/src/asset_map.cc b/src/asset_map.cc new file mode 100644 index 00000000..763d348e --- /dev/null +++ b/src/asset_map.cc @@ -0,0 +1,204 @@ +/* + Copyright (C) 2022 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include "asset_map.h" +#include "dcp_assert.h" +#include "raw_convert.h" +#include "util.h" +#include "warnings.h" +LIBDCP_DISABLE_WARNINGS +#include <libxml++/libxml++.h> +LIBDCP_ENABLE_WARNINGS +#include <boost/algorithm/string.hpp> + + +using std::string; +using std::vector; +using boost::algorithm::starts_with; +using namespace dcp; + + +static string const assetmap_interop_ns = "http://www.digicine.com/PROTO-ASDCP-AM-20040311#"; +static string const assetmap_smpte_ns = "http://www.smpte-ra.org/schemas/429-9/2007/AM"; + + +AssetMap::AssetMap(boost::filesystem::path file) + : _file(file) +{ + cxml::Document doc("AssetMap"); + doc.read_file(file); + + if (doc.namespace_uri() == assetmap_interop_ns) { + _standard = Standard::INTEROP; + } else if (doc.namespace_uri() == assetmap_smpte_ns) { + _standard = Standard::SMPTE; + } else { + boost::throw_exception(XMLError("Unrecognised AssetMap namespace " + doc.namespace_uri())); + } + + _annotation_text = doc.optional_string_child("AnnotationText"); + _issue_date = doc.string_child("IssueDate"); + _issuer = doc.string_child("Issuer"); + _creator = doc.string_child("Creator"); + + for (auto asset: doc.node_child("AssetList")->node_children("Asset")) { + _assets.push_back(Asset(asset, _file->parent_path(), _standard)); + } +} + + +vector<boost::filesystem::path> +AssetMap::pkl_paths() const +{ + auto paths = std::vector<boost::filesystem::path>(); + for (auto asset: _assets) { + if (asset.pkl()) { + paths.push_back(asset.path()); + } + } + return paths; +} + + +std::map<std::string, boost::filesystem::path> +AssetMap::paths() const +{ + auto paths = std::map<string, boost::filesystem::path>(); + for (auto asset: _assets) { + paths[asset.id()] = asset.path(); + } + return paths; +} + + +void +AssetMap::add_asset(string id, boost::filesystem::path path, bool pkl) +{ + _assets.push_back(Asset(id, path, pkl)); +} + + +void +AssetMap::write_xml(boost::filesystem::path file) +{ + xmlpp::Document doc; + xmlpp::Element* root; + + switch (_standard) { + case Standard::INTEROP: + root = doc.create_root_node("AssetMap", assetmap_interop_ns); + break; + case Standard::SMPTE: + root = doc.create_root_node("AssetMap", assetmap_smpte_ns); + break; + default: + DCP_ASSERT (false); + } + + root->add_child("Id")->add_child_text("urn:uuid:" + _id); + if (_annotation_text) { + root->add_child("AnnotationText")->add_child_text(*_annotation_text); + } + + switch (_standard) { + case Standard::INTEROP: + root->add_child("VolumeCount")->add_child_text("1"); + root->add_child("IssueDate")->add_child_text(_issue_date); + root->add_child("Issuer")->add_child_text(_issuer); + root->add_child("Creator")->add_child_text(_creator); + break; + case Standard::SMPTE: + root->add_child("Creator")->add_child_text(_creator); + root->add_child("VolumeCount")->add_child_text ("1"); + root->add_child("IssueDate")->add_child_text(_issue_date); + root->add_child("Issuer")->add_child_text(_issuer); + break; + default: + DCP_ASSERT (false); + } + + auto const dcp_root_directory = file.parent_path(); + + auto asset_list = root->add_child("AssetList"); + + for (auto asset: _assets) { + auto node = asset_list->add_child("Asset"); + node->add_child("Id")->add_child_text("urn:uuid:" + asset.id()); + if (asset.pkl()) { + node->add_child("PackingList")->add_child_text("true"); + } + auto chunk_list = node->add_child("ChunkList"); + auto chunk = chunk_list->add_child("Chunk"); + + auto relative_path = relative_to_root(dcp_root_directory, asset.path()); + DCP_ASSERT(relative_path); + + chunk->add_child("Path")->add_child_text(relative_path->generic_string()); + chunk->add_child("VolumeIndex")->add_child_text("1"); + chunk->add_child("Offset")->add_child_text("0"); + chunk->add_child("Length")->add_child_text(raw_convert<string>(boost::filesystem::file_size(asset.path()))); + } + + doc.write_to_file_formatted(file.string(), "UTF-8"); + _file = file; +} + + +AssetMap::Asset::Asset(cxml::ConstNodePtr node, boost::filesystem::path root, dcp::Standard standard) + : Object(remove_urn_uuid(node->string_child("Id"))) +{ + if (node->node_child("ChunkList")->node_children("Chunk").size() != 1) { + boost::throw_exception(XMLError("unsupported asset chunk count")); + } + + auto path_from_xml = node->node_child("ChunkList")->node_child("Chunk")->string_child("Path"); + if (starts_with(path_from_xml, "file://")) { + path_from_xml = path_from_xml.substr(7); + } + + _path = root / path_from_xml; + + switch (standard) { + case Standard::INTEROP: + _pkl = static_cast<bool>(node->optional_node_child("PackingList")); + break; + case Standard::SMPTE: + { + auto pkl_bool = node->optional_string_child("PackingList"); + _pkl = pkl_bool && *pkl_bool == "true"; + break; + } + } +} + diff --git a/src/asset_map.h b/src/asset_map.h new file mode 100644 index 00000000..906418b4 --- /dev/null +++ b/src/asset_map.h @@ -0,0 +1,110 @@ +/* + Copyright (C) 2022 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include "exceptions.h" +#include "object.h" +#include "types.h" +#include <boost/optional.hpp> +#include <string> + + +namespace dcp { + + +class AssetMap : public Object +{ +public: + AssetMap(Standard standard, boost::optional<std::string> annotation_text, std::string issue_date, std::string issuer, std::string creator) + : _standard(standard) + , _annotation_text(annotation_text) + , _issue_date(issue_date) + , _issuer(issuer) + , _creator(creator) + {} + + explicit AssetMap(boost::filesystem::path file); + + void add_asset(std::string id, boost::filesystem::path path, bool pkl); + + boost::optional<boost::filesystem::path> path() const { + return _file; + } + + std::vector<boost::filesystem::path> pkl_paths() const; + std::map<std::string, boost::filesystem::path> paths() const; + dcp::Standard standard() const { + return _standard; + } + + void write_xml(boost::filesystem::path file); + + class Asset : public Object + { + public: + Asset(std::string id, boost::filesystem::path path, bool pkl) + : Object(id) + , _path(path) + , _pkl(pkl) + {} + + Asset(cxml::ConstNodePtr node, boost::filesystem::path root, dcp::Standard standard); + + boost::filesystem::path path() const { + return _path; + } + + bool pkl() const { + return _pkl; + } + + private: + boost::filesystem::path _path; + bool _pkl = false; + }; + +private: + Standard _standard; + boost::optional<std::string> _annotation_text; + std::string _issue_date; + std::string _issuer; + std::string _creator; + std::vector<Asset> _assets; + /** The most recent disk file used to read or write this AssetMap */ + mutable boost::optional<boost::filesystem::path> _file; +}; + + +} + + @@ -91,8 +91,6 @@ using boost::optional; using namespace dcp; -static string const assetmap_interop_ns = "http://www.digicine.com/PROTO-ASDCP-AM-20040311#"; -static string const assetmap_smpte_ns = "http://www.smpte-ra.org/schemas/429-9/2007/AM"; static string const volindex_interop_ns = "http://www.digicine.com/PROTO-ASDCP-VL-20040311#"; static string const volindex_smpte_ns = "http://www.smpte-ra.org/schemas/429-9/2007/AM"; @@ -110,10 +108,9 @@ DCP::DCP (boost::filesystem::path directory) DCP::DCP (DCP&& other) : _directory(std::move(other._directory)) + , _asset_map(std::move(other._asset_map)) , _cpls(std::move(other._cpls)) , _pkls(std::move(other._pkls)) - , _asset_map(std::move(other._asset_map)) - , _standard(std::move(other._standard)) { } @@ -122,10 +119,9 @@ DCP::DCP (DCP&& other) dcp::DCP& DCP::operator= (DCP&& other) { _directory = std::move(other._directory); + _asset_map = std::move(other._asset_map); _cpls = std::move(other._cpls); _pkls = std::move(other._pkls); - _asset_map = std::move(other._asset_map); - _standard = std::move(other._standard); return *this; } @@ -135,63 +131,26 @@ DCP::read (vector<dcp::VerificationNote>* notes, bool ignore_incorrect_picture_m { /* Read the ASSETMAP and PKL */ + boost::filesystem::path asset_map_path; + if (boost::filesystem::exists (_directory / "ASSETMAP")) { - _asset_map = _directory / "ASSETMAP"; + asset_map_path = _directory / "ASSETMAP"; } else if (boost::filesystem::exists (_directory / "ASSETMAP.xml")) { - _asset_map = _directory / "ASSETMAP.xml"; + asset_map_path = _directory / "ASSETMAP.xml"; } else { boost::throw_exception (MissingAssetmapError(_directory)); } - cxml::Document asset_map ("AssetMap"); - - asset_map.read_file (_asset_map.get()); - if (asset_map.namespace_uri() == assetmap_interop_ns) { - _standard = Standard::INTEROP; - } else if (asset_map.namespace_uri() == assetmap_smpte_ns) { - _standard = Standard::SMPTE; - } else { - boost::throw_exception (XMLError ("Unrecognised Assetmap namespace " + asset_map.namespace_uri())); - } - - auto asset_nodes = asset_map.node_child("AssetList")->node_children ("Asset"); - map<string, boost::filesystem::path> paths; - vector<boost::filesystem::path> pkl_paths; - for (auto i: asset_nodes) { - if (i->node_child("ChunkList")->node_children("Chunk").size() != 1) { - boost::throw_exception (XMLError ("unsupported asset chunk count")); - } - auto p = i->node_child("ChunkList")->node_child("Chunk")->string_child ("Path"); - if (starts_with (p, "file://")) { - p = p.substr (7); - } - switch (*_standard) { - case Standard::INTEROP: - if (i->optional_node_child("PackingList")) { - pkl_paths.push_back (p); - } else { - paths.insert (make_pair(remove_urn_uuid(i->string_child("Id")), p)); - } - break; - case Standard::SMPTE: - { - auto pkl_bool = i->optional_string_child("PackingList"); - if (pkl_bool && *pkl_bool == "true") { - pkl_paths.push_back (p); - } else { - paths.insert (make_pair(remove_urn_uuid(i->string_child("Id")), p)); - } - break; - } - } - } + _asset_map = AssetMap(asset_map_path); + auto const pkl_paths = _asset_map->pkl_paths(); + auto const standard = _asset_map->standard(); if (pkl_paths.empty()) { boost::throw_exception (XMLError ("No packing lists found in asset map")); } for (auto i: pkl_paths) { - _pkls.push_back (make_shared<PKL>(_directory / i)); + _pkls.push_back(make_shared<PKL>(i)); } /* Now we have: @@ -206,10 +165,9 @@ DCP::read (vector<dcp::VerificationNote>* notes, bool ignore_incorrect_picture_m */ vector<shared_ptr<Asset>> other_assets; - for (auto i: paths) { - auto path = _directory / i.second; - - if (i.second.empty()) { + auto paths = _asset_map->paths(); + for (auto path: paths) { + if (path.second == _directory) { /* I can't see how this is valid, but it's been seen in the wild with a DCP that claims to come from ClipsterDCI 5.10.0.5. @@ -220,9 +178,9 @@ DCP::read (vector<dcp::VerificationNote>* notes, bool ignore_incorrect_picture_m continue; } - if (!boost::filesystem::exists(path)) { + if (!boost::filesystem::exists(path.second)) { if (notes) { - notes->push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_ASSET, path}); + notes->push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_ASSET, path.second}); } continue; } @@ -230,7 +188,7 @@ DCP::read (vector<dcp::VerificationNote>* notes, bool ignore_incorrect_picture_m /* Find the <Type> for this asset from the PKL that contains the asset */ optional<string> pkl_type; for (auto j: _pkls) { - pkl_type = j->type(i.first); + pkl_type = j->type(path.first); if (pkl_type) { break; } @@ -251,45 +209,45 @@ DCP::read (vector<dcp::VerificationNote>* notes, bool ignore_incorrect_picture_m pkl_type = pkl_type->substr(0, pkl_type->find(";")); if ( - pkl_type == remove_parameters(CPL::static_pkl_type(*_standard)) || - pkl_type == remove_parameters(InteropSubtitleAsset::static_pkl_type(*_standard))) { + pkl_type == remove_parameters(CPL::static_pkl_type(standard)) || + pkl_type == remove_parameters(InteropSubtitleAsset::static_pkl_type(standard))) { auto p = new xmlpp::DomParser; try { - p->parse_file (path.string()); + p->parse_file (path.second.string()); } catch (std::exception& e) { delete p; - throw ReadError(String::compose("XML error in %1", path.string()), e.what()); + throw ReadError(String::compose("XML error in %1", path.second.string()), e.what()); } auto const root = p->get_document()->get_root_node()->get_name(); delete p; if (root == "CompositionPlaylist") { - auto cpl = make_shared<CPL>(path); - if (_standard && cpl->standard() != _standard.get() && notes) { + auto cpl = make_shared<CPL>(path.second); + if (cpl->standard() != standard && notes) { notes->push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_STANDARD}); } _cpls.push_back (cpl); } else if (root == "DCSubtitle") { - if (_standard && _standard.get() == Standard::SMPTE && notes) { + if (standard == Standard::SMPTE && notes) { notes->push_back (VerificationNote(VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_STANDARD)); } - other_assets.push_back (make_shared<InteropSubtitleAsset>(path)); + other_assets.push_back (make_shared<InteropSubtitleAsset>(path.second)); } } else if ( - *pkl_type == remove_parameters(PictureAsset::static_pkl_type(*_standard)) || - *pkl_type == remove_parameters(SoundAsset::static_pkl_type(*_standard)) || - *pkl_type == remove_parameters(AtmosAsset::static_pkl_type(*_standard)) || - *pkl_type == remove_parameters(SMPTESubtitleAsset::static_pkl_type(*_standard)) + *pkl_type == remove_parameters(PictureAsset::static_pkl_type(standard)) || + *pkl_type == remove_parameters(SoundAsset::static_pkl_type(standard)) || + *pkl_type == remove_parameters(AtmosAsset::static_pkl_type(standard)) || + *pkl_type == remove_parameters(SMPTESubtitleAsset::static_pkl_type(standard)) ) { bool found_threed_marked_as_twod = false; - other_assets.push_back (asset_factory(path, ignore_incorrect_picture_mxf_type, &found_threed_marked_as_twod)); + other_assets.push_back (asset_factory(path.second, ignore_incorrect_picture_mxf_type, &found_threed_marked_as_twod)); if (found_threed_marked_as_twod && notes) { - notes->push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::THREED_ASSET_MARKED_AS_TWOD, path}); + notes->push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::THREED_ASSET_MARKED_AS_TWOD, path.second}); } - } else if (*pkl_type == remove_parameters(FontAsset::static_pkl_type(*_standard))) { - other_assets.push_back (make_shared<FontAsset>(i.first, path)); + } else if (*pkl_type == remove_parameters(FontAsset::static_pkl_type(standard))) { + other_assets.push_back (make_shared<FontAsset>(path.first, path.second)); } else if (*pkl_type == "image/png") { /* It's an Interop PNG subtitle; let it go */ } else { @@ -432,80 +390,6 @@ DCP::write_volindex (Standard standard) const void -DCP::write_assetmap ( - Standard standard, string pkl_uuid, boost::filesystem::path pkl_path, - string issuer, string creator, string issue_date, string annotation_text - ) const -{ - auto p = _directory; - - switch (standard) { - case Standard::INTEROP: - p /= "ASSETMAP"; - break; - case Standard::SMPTE: - p /= "ASSETMAP.xml"; - break; - default: - DCP_ASSERT (false); - } - - xmlpp::Document doc; - xmlpp::Element* root; - - switch (standard) { - case Standard::INTEROP: - root = doc.create_root_node ("AssetMap", assetmap_interop_ns); - break; - case Standard::SMPTE: - root = doc.create_root_node ("AssetMap", assetmap_smpte_ns); - break; - default: - DCP_ASSERT (false); - } - - root->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid()); - root->add_child("AnnotationText")->add_child_text (annotation_text); - - switch (standard) { - case Standard::INTEROP: - root->add_child("VolumeCount")->add_child_text ("1"); - root->add_child("IssueDate")->add_child_text (issue_date); - root->add_child("Issuer")->add_child_text (issuer); - root->add_child("Creator")->add_child_text (creator); - break; - case Standard::SMPTE: - root->add_child("Creator")->add_child_text (creator); - root->add_child("VolumeCount")->add_child_text ("1"); - root->add_child("IssueDate")->add_child_text (issue_date); - root->add_child("Issuer")->add_child_text (issuer); - break; - default: - DCP_ASSERT (false); - } - - auto asset_list = root->add_child ("AssetList"); - - auto asset = asset_list->add_child ("Asset"); - asset->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid); - asset->add_child("PackingList")->add_child_text ("true"); - auto chunk_list = asset->add_child ("ChunkList"); - auto chunk = chunk_list->add_child ("Chunk"); - chunk->add_child("Path")->add_child_text (pkl_path.filename().string()); - chunk->add_child("VolumeIndex")->add_child_text ("1"); - chunk->add_child("Offset")->add_child_text ("0"); - chunk->add_child("Length")->add_child_text (raw_convert<string> (boost::filesystem::file_size (pkl_path))); - - for (auto i: assets()) { - i->write_to_assetmap (asset_list, _directory); - } - - doc.write_to_file_formatted (p.string (), "UTF-8"); - _asset_map = p; -} - - -void DCP::write_xml ( string issuer, string creator, @@ -553,7 +437,17 @@ DCP::write_xml ( pkl->write_xml (pkl_path, signer); write_volindex (standard); - write_assetmap (standard, pkl->id(), pkl_path, issuer, creator, issue_date, annotation_text); + + if (!_asset_map) { + _asset_map = AssetMap(standard, annotation_text, issue_date, issuer, creator); + _asset_map->add_asset(pkl->id(), pkl_path, true); + for (auto asset: assets()) { + asset->add_to_assetmap(*_asset_map, _directory); + } + } + + auto asset_map_path = _directory / (standard == Standard::INTEROP ? "ASSETMAP" : "ASSETMAP.xml"); + _asset_map->write_xml(asset_map_path); } @@ -41,6 +41,7 @@ #define LIBDCP_DCP_H +#include "asset_map.h" #include "certificate.h" #include "compose.hpp" #include "metadata.h" @@ -165,7 +166,11 @@ public: /** @return Standard of a DCP that was read in */ boost::optional<Standard> standard () const { - return _standard; + if (!_asset_map) { + return {}; + } + + return _asset_map->standard(); } boost::filesystem::path directory () const { @@ -180,7 +185,11 @@ public: } boost::optional<boost::filesystem::path> asset_map_path () { - return _asset_map; + if (!_asset_map) { + return {}; + } + + return _asset_map->path(); } static std::vector<boost::filesystem::path> directories_from_files (std::vector<boost::filesystem::path> files); @@ -200,15 +209,11 @@ private: /** The directory that we are writing to */ boost::filesystem::path _directory; + boost::optional<AssetMap> _asset_map; /** The CPLs that make up this DCP */ std::vector<std::shared_ptr<CPL>> _cpls; /** The PKLs that make up this DCP */ std::vector<std::shared_ptr<PKL>> _pkls; - /** File that the ASSETMAP was read from or last written to */ - mutable boost::optional<boost::filesystem::path> _asset_map; - - /** Standard of DCP that was read in */ - boost::optional<Standard> _standard; }; diff --git a/src/interop_subtitle_asset.cc b/src/interop_subtitle_asset.cc index 798f1cda..a22142dc 100644 --- a/src/interop_subtitle_asset.cc +++ b/src/interop_subtitle_asset.cc @@ -267,15 +267,15 @@ InteropSubtitleAsset::add_font_assets (vector<shared_ptr<Asset>>& assets) void -InteropSubtitleAsset::write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const +InteropSubtitleAsset::add_to_assetmap (AssetMap& asset_map, boost::filesystem::path root) const { - Asset::write_to_assetmap (node, root); + Asset::add_to_assetmap (asset_map, root); for (auto i: _subtitles) { auto im = dynamic_pointer_cast<dcp::SubtitleImage> (i); if (im) { - DCP_ASSERT (im->file()); - write_file_to_assetmap (node, root, im->file().get(), im->id()); + DCP_ASSERT(im->file()); + add_file_to_assetmap(asset_map, root, im->file().get(), im->id()); } } } diff --git a/src/interop_subtitle_asset.h b/src/interop_subtitle_asset.h index b953b303..29f944b6 100644 --- a/src/interop_subtitle_asset.h +++ b/src/interop_subtitle_asset.h @@ -48,6 +48,7 @@ namespace dcp { +class AssetMap; class InteropLoadFontNode; @@ -68,7 +69,7 @@ public: NoteHandler note ) const override; - void write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const override; + void add_to_assetmap (AssetMap& asset_map, boost::filesystem::path root) const override; void add_to_pkl (std::shared_ptr<PKL> pkl, boost::filesystem::path root) const override; std::vector<std::shared_ptr<LoadFontNode>> load_font_nodes () const override; diff --git a/src/wscript b/src/wscript index da4d0d1e..5e118c6e 100644 --- a/src/wscript +++ b/src/wscript @@ -37,6 +37,7 @@ def build(bld): array_data.cc asset.cc asset_factory.cc + asset_map.cc asset_writer.cc atmos_asset.cc atmos_asset_writer.cc diff --git a/test/util_test.cc b/test/util_test.cc index 8ca7df44..092769c8 100644 --- a/test/util_test.cc +++ b/test/util_test.cc @@ -93,42 +93,58 @@ BOOST_AUTO_TEST_CASE (content_kind_test) /** Test dcp::relative_to_root */ BOOST_AUTO_TEST_CASE (relative_to_root_test) { + using namespace boost::filesystem; + + path const base = "build/test/relative_to_root_test"; + { - boost::filesystem::path root = "a"; + path root = base / "a"; root /= "b"; - boost::filesystem::path file = "a"; + path file = base / "a"; file /= "b"; file /= "c"; - boost::optional<boost::filesystem::path> rel = dcp::relative_to_root (root, file); + boost::system::error_code ec; + create_directories(root, ec); + create_directories(file, ec); + + auto rel = dcp::relative_to_root (root, file); BOOST_CHECK (rel); - BOOST_CHECK_EQUAL (rel.get(), boost::filesystem::path ("c")); + BOOST_CHECK_EQUAL (rel.get(), path("c")); } { - boost::filesystem::path root = "a"; + path root = "a"; root /= "b"; root /= "c"; - boost::filesystem::path file = "a"; + path file = "a"; file /= "b"; - boost::optional<boost::filesystem::path> rel = dcp::relative_to_root (root, file); + boost::system::error_code ec; + create_directories(root, ec); + create_directories(file, ec); + + auto rel = dcp::relative_to_root (root, file); BOOST_CHECK (!rel); } { - boost::filesystem::path root = "a"; + path root = "a"; - boost::filesystem::path file = "a"; + path file = "a"; file /= "b"; file /= "c"; - boost::optional<boost::filesystem::path> rel = dcp::relative_to_root (root, file); + boost::system::error_code ec; + create_directories(root, ec); + create_directories(file, ec); + + auto rel = dcp::relative_to_root (root, file); BOOST_CHECK (rel); - boost::filesystem::path check = "b"; + path check = "b"; check /= "c"; BOOST_CHECK_EQUAL (rel.get(), check); } |
