2 Copyright (C) 2012-2022 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
35 #include "asset_map.h"
36 #include "dcp_assert.h"
37 #include "exceptions.h"
38 #include "filesystem.h"
39 #include "raw_convert.h"
41 LIBDCP_DISABLE_WARNINGS
42 #include <libxml++/libxml++.h>
43 LIBDCP_ENABLE_WARNINGS
44 #include <boost/algorithm/string.hpp>
51 using boost::algorithm::starts_with;
55 static string const assetmap_interop_ns = "http://www.digicine.com/PROTO-ASDCP-AM-20040311#";
56 static string const assetmap_smpte_ns = "http://www.smpte-ra.org/schemas/429-9/2007/AM";
59 AssetMap::AssetMap(boost::filesystem::path file)
62 cxml::Document doc("AssetMap");
64 doc.read_file(dcp::filesystem::fix_long_path(file));
65 if (doc.namespace_uri() == assetmap_interop_ns) {
66 _standard = Standard::INTEROP;
67 } else if (doc.namespace_uri() == assetmap_smpte_ns) {
68 _standard = Standard::SMPTE;
70 boost::throw_exception(XMLError("Unrecognised Assetmap namespace " + doc.namespace_uri()));
73 _id = remove_urn_uuid(doc.string_child("Id"));
74 _annotation_text = doc.optional_string_child("AnnotationText");
75 _issue_date = doc.string_child("IssueDate");
76 _issuer = doc.string_child("Issuer");
77 _creator = doc.string_child("Creator");
79 for (auto asset: doc.node_child("AssetList")->node_children("Asset")) {
80 _assets.push_back(Asset(asset, _file->parent_path(), _standard));
85 AssetMap::Asset::Asset(cxml::ConstNodePtr node, boost::filesystem::path root, dcp::Standard standard)
86 : Object(remove_urn_uuid(node->string_child("Id")))
88 if (node->node_child("ChunkList")->node_children("Chunk").size() != 1) {
89 boost::throw_exception (XMLError ("unsupported asset chunk count"));
92 auto path_from_xml = node->node_child("ChunkList")->node_child("Chunk")->string_child("Path");
93 if (starts_with(path_from_xml, "file://")) {
94 path_from_xml = path_from_xml.substr(7);
97 _path = root / path_from_xml;
100 case Standard::INTEROP:
101 _pkl = static_cast<bool>(node->optional_node_child("PackingList"));
103 case Standard::SMPTE:
105 auto pkl_bool = node->optional_string_child("PackingList");
106 _pkl = pkl_bool && *pkl_bool == "true";
114 AssetMap::add_asset(string id, boost::filesystem::path path, bool pkl)
116 _assets.push_back(Asset(id, path, pkl));
121 AssetMap::clear_assets()
127 map<std::string, boost::filesystem::path>
128 AssetMap::asset_ids_and_paths() const
130 auto paths = map<string, boost::filesystem::path>();
131 for (auto asset: _assets) {
132 paths[asset.id()] = asset.path();
138 vector<boost::filesystem::path>
139 AssetMap::pkl_paths() const
141 auto paths = std::vector<boost::filesystem::path>();
142 for (auto asset: _assets) {
144 paths.push_back(asset.path());
152 AssetMap::write_xml(boost::filesystem::path file) const
155 xmlpp::Element* root;
158 case Standard::INTEROP:
159 root = doc.create_root_node("AssetMap", assetmap_interop_ns);
161 case Standard::SMPTE:
162 root = doc.create_root_node("AssetMap", assetmap_smpte_ns);
168 root->add_child("Id")->add_child_text("urn:uuid:" + _id);
169 if (_annotation_text) {
170 root->add_child("AnnotationText")->add_child_text(*_annotation_text);
174 case Standard::INTEROP:
175 root->add_child("VolumeCount")->add_child_text("1");
176 root->add_child("IssueDate")->add_child_text(_issue_date);
177 root->add_child("Issuer")->add_child_text(_issuer);
178 root->add_child("Creator")->add_child_text(_creator);
180 case Standard::SMPTE:
181 root->add_child("Creator")->add_child_text(_creator);
182 root->add_child("VolumeCount")->add_child_text("1");
183 root->add_child("IssueDate")->add_child_text(_issue_date);
184 root->add_child("Issuer")->add_child_text(_issuer);
190 auto asset_list = root->add_child("AssetList");
191 for (auto const& asset: _assets) {
192 asset.write_xml(asset_list, file.parent_path());
195 doc.write_to_file_formatted(dcp::filesystem::fix_long_path(file).string(), "UTF-8");
201 AssetMap::Asset::write_xml(xmlpp::Element* asset_list, boost::filesystem::path dcp_root_directory) const
203 auto node = asset_list->add_child("Asset");
204 node->add_child("Id")->add_child_text("urn:uuid:" + _id);
206 node->add_child("PackingList")->add_child_text("true");
208 auto chunk_list = node->add_child("ChunkList");
209 auto chunk = chunk_list->add_child("Chunk");
211 auto relative_path = relative_to_root(filesystem::canonical(dcp_root_directory), filesystem::canonical(_path));
212 DCP_ASSERT(relative_path);
214 chunk->add_child("Path")->add_child_text(relative_path->generic_string());
215 chunk->add_child("VolumeIndex")->add_child_text("1");
216 chunk->add_child("Offset")->add_child_text("0");
217 chunk->add_child("Length")->add_child_text(raw_convert<string>(filesystem::file_size(_path)));