2 Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "package_base.h"
21 #include "compose.hpp"
22 #include "exceptions.h"
24 #include "dcp_assert.h"
25 #include "font_asset.h"
26 #include "raw_convert.h"
27 #include <libcxml/cxml.h>
28 #include <libxml++/libxml++.h>
29 #include <boost/foreach.hpp>
30 #include <boost/algorithm/string.hpp>
35 using boost::shared_ptr;
36 using boost::algorithm::starts_with;
39 PackageBase::PackageBase (boost::filesystem::path directory)
41 if (!boost::filesystem::exists (directory)) {
42 boost::filesystem::create_directories (directory);
45 _directory = boost::filesystem::canonical (directory);
48 list<shared_ptr<Asset> >
49 PackageBase::read_assetmap (bool keep_going, ReadErrors* errors) const
51 boost::filesystem::path asset_map_file;
52 if (boost::filesystem::exists (_directory / "ASSETMAP")) {
53 asset_map_file = _directory / "ASSETMAP";
54 } else if (boost::filesystem::exists (_directory / "ASSETMAP.xml")) {
55 asset_map_file = _directory / "ASSETMAP.xml";
57 boost::throw_exception (PackageReadError (String::compose ("could not find AssetMap file in `%1'", _directory.string())));
60 cxml::Document asset_map ("AssetMap");
61 asset_map.read_file (asset_map_file);
62 list<shared_ptr<cxml::Node> > asset_nodes = asset_map.node_child("AssetList")->node_children ("Asset");
63 map<string, boost::filesystem::path> paths;
64 BOOST_FOREACH (shared_ptr<cxml::Node> i, asset_nodes) {
65 if (i->node_child("ChunkList")->node_children("Chunk").size() != 1) {
66 boost::throw_exception (XMLError ("unsupported asset chunk count"));
68 string p = i->node_child("ChunkList")->node_child("Chunk")->string_child ("Path");
69 if (starts_with (p, "file://")) {
72 paths.insert (make_pair (remove_urn_uuid (i->string_child ("Id")), p));
75 /* Read all the assets from the asset map */
76 /* XXX: I think we should be looking at the PKL here to decide type, not
77 the extension of the file.
80 list<shared_ptr<Asset> > assets;
82 for (map<string, boost::filesystem::path>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
83 boost::filesystem::path path = _directory / i->second;
85 if (!boost::filesystem::exists (path)) {
88 errors->push_back (shared_ptr<MissingAssetError> (new MissingAssetError (path)));
91 throw MissingAssetError (path);
96 if (boost::filesystem::extension(path) == ".xml") {
97 xmlpp::DomParser* p = new xmlpp::DomParser;
99 p->parse_file (path.string());
100 } catch (std::exception& e) {
105 string const root = p->get_document()->get_root_node()->get_name ();
108 shared_ptr<Asset> a = xml_asset_factory (path, root);
110 assets.push_back (a);
112 } else if (boost::filesystem::extension (path) == ".mxf") {
113 assets.push_back (mxf_asset_factory (path));
114 } else if (boost::filesystem::extension (path) == ".ttf") {
115 assets.push_back (shared_ptr<FontAsset> (new FontAsset (i->first, path)));
123 /** Write the VOLINDEX file.
124 * @param standard DCP standard to use (INTEROP or SMPTE)
127 PackageBase::write_volindex (Standard standard) const
129 boost::filesystem::path p = _directory;
144 xmlpp::Element* root = 0;
148 root = doc.create_root_node ("VolumeIndex", "http://www.digicine.com/PROTO-ASDCP-AM-20040311#");
152 root = doc.create_root_node ("VolumeIndex", "http://www.smpte-ra.org/schemas/429-9/2007/AM");
156 root->add_child("Index")->add_child_text ("1");
157 doc.write_to_file (p.string (), "UTF-8");
161 PackageBase::write_assetmap (Standard standard, string pkl_uuid, int pkl_length, dcp::XMLMetadata metadata) const
163 boost::filesystem::path p = _directory;
176 xmlpp::Element* root = 0;
180 root = doc.create_root_node ("AssetMap", "http://www.digicine.com/PROTO-ASDCP-AM-20040311#");
184 root = doc.create_root_node ("AssetMap", "http://www.smpte-ra.org/schemas/429-9/2007/AM");
188 root->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid());
189 root->add_child("AnnotationText")->add_child_text ("Created by " + metadata.creator);
193 root->add_child("VolumeCount")->add_child_text ("1");
194 root->add_child("IssueDate")->add_child_text (metadata.issue_date);
195 root->add_child("Issuer")->add_child_text (metadata.issuer);
196 root->add_child("Creator")->add_child_text (metadata.creator);
200 root->add_child("Creator")->add_child_text (metadata.creator);
201 root->add_child("VolumeCount")->add_child_text ("1");
202 root->add_child("IssueDate")->add_child_text (metadata.issue_date);
203 root->add_child("Issuer")->add_child_text (metadata.issuer);
207 xmlpp::Node* asset_list = root->add_child ("AssetList");
209 xmlpp::Node* asset = asset_list->add_child ("Asset");
210 asset->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid);
211 asset->add_child("PackingList")->add_child_text ("true");
212 xmlpp::Node* chunk_list = asset->add_child ("ChunkList");
213 xmlpp::Node* chunk = chunk_list->add_child ("Chunk");
214 chunk->add_child("Path")->add_child_text ("pkl_" + pkl_uuid + ".xml");
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> (pkl_length));
219 BOOST_FOREACH (shared_ptr<dcp::Asset> i, assets ()) {
220 i->write_to_assetmap (asset_list, _directory);
223 /* This must not be the _formatted version otherwise signature digests will be wrong */
224 doc.write_to_file (p.string (), "UTF-8");