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 "raw_convert.h"
40 LIBDCP_DISABLE_WARNINGS
41 #include <libxml++/libxml++.h>
42 LIBDCP_ENABLE_WARNINGS
43 #include <boost/algorithm/string.hpp>
50 using boost::algorithm::starts_with;
54 static string const assetmap_interop_ns = "http://www.digicine.com/PROTO-ASDCP-AM-20040311#";
55 static string const assetmap_smpte_ns = "http://www.smpte-ra.org/schemas/429-9/2007/AM";
58 AssetMap::AssetMap(boost::filesystem::path file)
61 cxml::Document doc("AssetMap");
64 if (doc.namespace_uri() == assetmap_interop_ns) {
65 _standard = Standard::INTEROP;
66 } else if (doc.namespace_uri() == assetmap_smpte_ns) {
67 _standard = Standard::SMPTE;
69 boost::throw_exception(XMLError("Unrecognised Assetmap namespace " + doc.namespace_uri()));
72 _id = remove_urn_uuid(doc.string_child("Id"));
73 _annotation_text = doc.optional_string_child("AnnotationText");
74 _issue_date = doc.string_child("IssueDate");
75 _issuer = doc.string_child("Issuer");
76 _creator = doc.string_child("Creator");
78 for (auto asset: doc.node_child("AssetList")->node_children("Asset")) {
79 _assets.push_back(Asset(asset, _file->parent_path(), _standard));
84 AssetMap::Asset::Asset(cxml::ConstNodePtr node, boost::filesystem::path root, dcp::Standard standard)
85 : Object(remove_urn_uuid(node->string_child("Id")))
87 if (node->node_child("ChunkList")->node_children("Chunk").size() != 1) {
88 boost::throw_exception (XMLError ("unsupported asset chunk count"));
91 auto path_from_xml = node->node_child("ChunkList")->node_child("Chunk")->string_child("Path");
92 if (starts_with(path_from_xml, "file://")) {
93 path_from_xml = path_from_xml.substr(7);
96 _path = root / path_from_xml;
99 case Standard::INTEROP:
100 _pkl = static_cast<bool>(node->optional_node_child("PackingList"));
102 case Standard::SMPTE:
104 auto pkl_bool = node->optional_string_child("PackingList");
105 _pkl = pkl_bool && *pkl_bool == "true";
113 AssetMap::add_asset(string id, boost::filesystem::path path, bool pkl)
115 _assets.push_back(Asset(id, path, pkl));
120 AssetMap::clear_assets()
126 map<std::string, boost::filesystem::path>
127 AssetMap::asset_ids_and_paths() const
129 auto paths = map<string, boost::filesystem::path>();
130 for (auto asset: _assets) {
131 paths[asset.id()] = asset.path();
137 vector<boost::filesystem::path>
138 AssetMap::pkl_paths() const
140 auto paths = std::vector<boost::filesystem::path>();
141 for (auto asset: _assets) {
143 paths.push_back(asset.path());
151 AssetMap::write_xml(boost::filesystem::path file) const
154 xmlpp::Element* root;
157 case Standard::INTEROP:
158 root = doc.create_root_node("AssetMap", assetmap_interop_ns);
160 case Standard::SMPTE:
161 root = doc.create_root_node("AssetMap", assetmap_smpte_ns);
167 root->add_child("Id")->add_child_text("urn:uuid:" + _id);
168 if (_annotation_text) {
169 root->add_child("AnnotationText")->add_child_text(*_annotation_text);
173 case Standard::INTEROP:
174 root->add_child("VolumeCount")->add_child_text("1");
175 root->add_child("IssueDate")->add_child_text(_issue_date);
176 root->add_child("Issuer")->add_child_text(_issuer);
177 root->add_child("Creator")->add_child_text(_creator);
179 case Standard::SMPTE:
180 root->add_child("Creator")->add_child_text(_creator);
181 root->add_child("VolumeCount")->add_child_text("1");
182 root->add_child("IssueDate")->add_child_text(_issue_date);
183 root->add_child("Issuer")->add_child_text(_issuer);
189 auto asset_list = root->add_child("AssetList");
190 for (auto const& asset: _assets) {
191 asset.write_xml(asset_list, file.parent_path());
194 doc.write_to_file_formatted(file.string(), "UTF-8");
200 AssetMap::Asset::write_xml(xmlpp::Element* asset_list, boost::filesystem::path dcp_root_directory) const
202 auto node = asset_list->add_child("Asset");
203 node->add_child("Id")->add_child_text("urn:uuid:" + _id);
205 node->add_child("PackingList")->add_child_text("true");
207 auto chunk_list = node->add_child("ChunkList");
208 auto chunk = chunk_list->add_child("Chunk");
210 auto relative_path = relative_to_root(boost::filesystem::canonical(dcp_root_directory), boost::filesystem::canonical(_path));
211 DCP_ASSERT(relative_path);
213 chunk->add_child("Path")->add_child_text(relative_path->generic_string());
214 chunk->add_child("VolumeIndex")->add_child_text("1");
215 chunk->add_child("Offset")->add_child_text("0");
216 chunk->add_child("Length")->add_child_text(raw_convert<string>(boost::filesystem::file_size(_path)));